<commit_before>package ecs

import (
	"testing"
)

func TestDisks(t *testing.T) {

	client := NewTestClient()

	instance, err := client.DescribeInstanceAttribute(TestInstanceId)
	if err != nil {
		t.Fatalf("Failed to DescribeInstanceAttribute for instance %s: %v", TestInstanceId, err)
	}

	args := DescribeDisksArgs{}

	args.InstanceId = TestInstanceId
	args.RegionId = instance.RegionId
	disks, _, err := client.DescribeDisks(&args)

	if err != nil {
		t.Fatalf("Failed to DescribeDisks for instance %s: %v", TestInstanceId, err)
	}

	for _, disk := range disks {
		t.Logf("Disk of instance %s: %++v", TestInstanceId, disk)
	}
}

func TestDiskCreationAndDeletion(t *testing.T) {

	if TestIAmRich == false { //Avoid payment
		return
	}

	client := NewTestClient()

	instance, err := client.DescribeInstanceAttribute(TestInstanceId)
	if err != nil {
		t.Fatalf("Failed to DescribeInstanceAttribute for instance %s: %v", TestInstanceId, err)
	}

	args := CreateDiskArgs{
		RegionId: instance.RegionId,
		ZoneId:   instance.ZoneId,
		DiskName: "test-disk",
		Size:     5,
	}

	diskId, err := client.CreateDisk(&args)
	if err != nil {
		t.Fatalf("Failed to create disk: %v", err)
	}
	t.Logf("Create disk %s successfully", diskId)

	attachArgs := AttachDiskArgs{
		InstanceId: instance.InstanceId,
		DiskId:     diskId,
	}

	err = client.AttachDisk(&attachArgs)
	if err != nil {
		t.Errorf("Failed to create disk: %v", err)
	} else {
		t.Logf("Attach disk %s to instance %s successfully", diskId, instance.InstanceId)

		instance, err = client.DescribeInstanceAttribute(TestInstanceId)
		if err != nil {
			t.Errorf("Failed to DescribeInstanceAttribute for instance %s: %v", TestInstanceId, err)
		} else {
			t.Logf("Instance: %++v  %v", instance, err)
		}
		err = client.WaitForDisk(instance.RegionId, diskId, DiskStatusInUse, 0)
		if err != nil {
			t.Fatalf("Failed to wait for disk %s to status %s: %v", diskId, DiskStatusInUse, err)
		}
		err = client.DetachDisk(instance.InstanceId, diskId)
		if err != nil {
			t.Errorf("Failed to detach disk: %v", err)
		} else {
			t.Logf("Detach disk %s to instance %s successfully", diskId, instance.InstanceId)
		}

		err = client.WaitForDisk(instance.RegionId, diskId, DiskStatusAvailable, 0)
		if err != nil {
			t.Fatalf("Failed to wait for disk %s to status %s: %v", diskId, DiskStatusAvailable, err)
		}
	}
	err = client.DeleteDisk(diskId)
	if err != nil {
		t.Fatalf("Failed to delete disk %s: %v", diskId, err)
	}
	t.Logf("Delete disk %s successfully", diskId)
}

func TestReplaceSystemDisk222(t *testing.T) {
	client := NewTestClientForDebug()

	args := ReplaceSystemDiskArgs{
		InstanceId: TestInstanceId,
		ImageId:    TestImageId,
		SystemDisk: SystemDiskType{
			Size: 192,
		},
		ClientToken: client.GenerateClientToken(),
	}

	diskId, err := client.ReplaceSystemDisk(&args)
	if err != nil {
		t.Errorf("Failed to replace system disk %v", err)
	} else {
		t.Logf("diskId is %s", diskId)
	}
}

func TestReplaceSystemDisk(t *testing.T) {
	client := NewTestClient()

	err := client.WaitForInstance(TestInstanceId, Running, 0)
	err = client.StopInstance(TestInstanceId, true)
	if err != nil {
		t.Errorf("Failed to stop instance %s: %v", TestInstanceId, err)
	}
	err = client.WaitForInstance(TestInstanceId, Stopped, 0)
	if err != nil {
		t.Errorf("Instance %s is failed to stop: %v", TestInstanceId, err)
	}
	t.Logf("Instance %s is stopped successfully.", TestInstanceId)

	args := ReplaceSystemDiskArgs{
		InstanceId: TestInstanceId,
		ImageId:    TestImageId,
	}

	diskId, err := client.ReplaceSystemDisk(&args)
	if err != nil {
		t.Errorf("Failed to replace system disk %v", err)
	}
	err = client.WaitForInstance(TestInstanceId, Stopped, 60)
	err = client.StartInstance(TestInstanceId)
	if err != nil {
		t.Errorf("Failed to start instance %s: %v", TestInstanceId, err)
	} else {
		err = client.WaitForInstance(TestInstanceId, Running, 0)
		if err != nil {
			t.Errorf("Failed to wait for instance %s running: %v", TestInstanceId, err)
		}
	}
	t.Logf("Replace system disk %s successfully ", diskId)
}
<commit_msg>Update disks_test.go<commit_after>package ecs

import (
	"testing"
)

func TestDisks(t *testing.T) {

	client := NewTestClient()

	instance, err := client.DescribeInstanceAttribute(TestInstanceId)
	if err != nil {
		t.Fatalf("Failed to DescribeInstanceAttribute for instance %s: %v", TestInstanceId, err)
	}

	args := DescribeDisksArgs{}

	args.InstanceId = TestInstanceId
	args.RegionId = instance.RegionId
	disks, _, err := client.DescribeDisks(&args)

	if err != nil {
		t.Fatalf("Failed to DescribeDisks for instance %s: %v", TestInstanceId, err)
	}

	for _, disk := range disks {
		t.Logf("Disk of instance %s: %++v", TestInstanceId, disk)
	}
}

func TestDiskCreationAndDeletion(t *testing.T) {

	if TestIAmRich == false { //Avoid payment
		return
	}

	client := NewTestClient()

	instance, err := client.DescribeInstanceAttribute(TestInstanceId)
	if err != nil {
		t.Fatalf("Failed to DescribeInstanceAttribute for instance %s: %v", TestInstanceId, err)
	}

	args := CreateDiskArgs{
		RegionId: instance.RegionId,
		ZoneId:   instance.ZoneId,
		DiskName: "test-disk",
		Size:     5,
	}

	diskId, err := client.CreateDisk(&args)
	if err != nil {
		t.Fatalf("Failed to create disk: %v", err)
	}
	t.Logf("Create disk %s successfully", diskId)

	attachArgs := AttachDiskArgs{
		InstanceId: instance.InstanceId,
		DiskId:     diskId,
	}

	err = client.AttachDisk(&attachArgs)
	if err != nil {
		t.Errorf("Failed to create disk: %v", err)
	} else {
		t.Logf("Attach disk %s to instance %s successfully", diskId, instance.InstanceId)

		instance, err = client.DescribeInstanceAttribute(TestInstanceId)
		if err != nil {
			t.Errorf("Failed to DescribeInstanceAttribute for instance %s: %v", TestInstanceId, err)
		} else {
			t.Logf("Instance: %++v  %v", instance, err)
		}
		err = client.WaitForDisk(instance.RegionId, diskId, DiskStatusInUse, 0)
		if err != nil {
			t.Fatalf("Failed to wait for disk %s to status %s: %v", diskId, DiskStatusInUse, err)
		}
		err = client.DetachDisk(instance.InstanceId, diskId)
		if err != nil {
			t.Errorf("Failed to detach disk: %v", err)
		} else {
			t.Logf("Detach disk %s to instance %s successfully", diskId, instance.InstanceId)
		}

		err = client.WaitForDisk(instance.RegionId, diskId, DiskStatusAvailable, 0)
		if err != nil {
			t.Fatalf("Failed to wait for disk %s to status %s: %v", diskId, DiskStatusAvailable, err)
		}
	}
	err = client.DeleteDisk(diskId)
	if err != nil {
		t.Fatalf("Failed to delete disk %s: %v", diskId, err)
	}
	t.Logf("Delete disk %s successfully", diskId)
}

func TestReplaceSystemDiskUsingSizeParam(t *testing.T) {
	client := NewTestClientForDebug()

	args := ReplaceSystemDiskArgs{
		InstanceId: TestInstanceId,
		ImageId:    TestImageId,
		SystemDisk: SystemDiskType{
			Size: 192,
		},
		ClientToken: client.GenerateClientToken(),
	}

	diskId, err := client.ReplaceSystemDisk(&args)
	if err != nil {
		t.Errorf("Failed to replace system disk %v", err)
	} else {
		t.Logf("diskId is %s", diskId)
	}
}

func TestReplaceSystemDisk(t *testing.T) {
	client := NewTestClient()

	err := client.WaitForInstance(TestInstanceId, Running, 0)
	err = client.StopInstance(TestInstanceId, true)
	if err != nil {
		t.Errorf("Failed to stop instance %s: %v", TestInstanceId, err)
	}
	err = client.WaitForInstance(TestInstanceId, Stopped, 0)
	if err != nil {
		t.Errorf("Instance %s is failed to stop: %v", TestInstanceId, err)
	}
	t.Logf("Instance %s is stopped successfully.", TestInstanceId)

	args := ReplaceSystemDiskArgs{
		InstanceId: TestInstanceId,
		ImageId:    TestImageId,
	}

	diskId, err := client.ReplaceSystemDisk(&args)
	if err != nil {
		t.Errorf("Failed to replace system disk %v", err)
	}
	err = client.WaitForInstance(TestInstanceId, Stopped, 60)
	err = client.StartInstance(TestInstanceId)
	if err != nil {
		t.Errorf("Failed to start instance %s: %v", TestInstanceId, err)
	} else {
		err = client.WaitForInstance(TestInstanceId, Running, 0)
		if err != nil {
			t.Errorf("Failed to wait for instance %s running: %v", TestInstanceId, err)
		}
	}
	t.Logf("Replace system disk %s successfully ", diskId)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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 clientcmd

import (
	"io/ioutil"
	"os"
	"strings"
	"testing"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

func TestConfirmUsableBadInfoButOkConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["missing ca"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: "missing",
	}
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		Username: "anything",
		Token:    "here",
	}
	config.Contexts["dirty"] = &clientcmdapi.Context{
		Cluster:  "missing ca",
		AuthInfo: "error",
	}
	config.Clusters["clean"] = &clientcmdapi.Cluster{
		Server: "anything",
	}
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		Token: "here",
	}
	config.Contexts["clean"] = &clientcmdapi.Context{
		Cluster:  "clean",
		AuthInfo: "clean",
	}

	badValidation := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read certificate-authority"},
	}
	okTest := configValidationTest{
		config: config,
	}

	okTest.testConfirmUsable("clean", t)
	badValidation.testConfig(t)
}
func TestConfirmUsableBadInfoConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["missing ca"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: "missing",
	}
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		Username: "anything",
		Token:    "here",
	}
	config.Contexts["first"] = &clientcmdapi.Context{
		Cluster:  "missing ca",
		AuthInfo: "error",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read certificate-authority"},
	}

	test.testConfirmUsable("first", t)
}
func TestConfirmUsableEmptyConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
	}

	test.testConfirmUsable("", t)
}
func TestConfirmUsableMissingConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
	}

	test.testConfirmUsable("not-here", t)
}
func TestValidateEmptyConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
	}

	test.testConfig(t)
}
func TestValidateMissingCurrentContextConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"context was not found for specified "},
	}

	test.testConfig(t)
}
func TestIsContextNotFound(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"

	err := Validate(*config)
	if !IsContextNotFound(err) {
		t.Errorf("Expected context not found, but got %v", err)
	}
	if !IsConfigurationInvalid(err) {
		t.Errorf("Expected configuration invalid, but got %v", err)
	}
}

func TestIsEmptyConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()

	err := Validate(*config)
	if !IsEmptyConfig(err) {
		t.Errorf("Expected context not found, but got %v", err)
	}
	if !IsConfigurationInvalid(err) {
		t.Errorf("Expected configuration invalid, but got %v", err)
	}
}

func TestIsConfigurationInvalid(t *testing.T) {
	if newErrConfigurationInvalid([]error{}) != nil {
		t.Errorf("unexpected error")
	}
	if newErrConfigurationInvalid([]error{ErrNoContext}) == ErrNoContext {
		t.Errorf("unexpected error")
	}
	if newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext}) == nil {
		t.Errorf("unexpected error")
	}
	if !IsConfigurationInvalid(newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext})) {
		t.Errorf("unexpected error")
	}
}

func TestValidateMissingReferencesConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"
	config.Contexts["anything"] = &clientcmdapi.Context{Cluster: "missing", AuthInfo: "missing"}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"user \"missing\" was not found for context \"anything\"", "cluster \"missing\" was not found for context \"anything\""},
	}

	test.testContext("anything", t)
	test.testConfig(t)
}
func TestValidateEmptyContext(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"
	config.Contexts["anything"] = &clientcmdapi.Context{}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"user was not specified for context \"anything\"", "cluster was not specified for context \"anything\""},
	}

	test.testContext("anything", t)
	test.testConfig(t)
}

func TestValidateEmptyClusterInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["empty"] = &clientcmdapi.Cluster{}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"cluster has no server defined"},
	}

	test.testCluster("empty", t)
	test.testConfig(t)
}

func TestValidateClusterInfoErrEmptyCluster(t *testing.T) {
	cluster := clientcmdapi.NewCluster()
	errs := validateClusterInfo("", *cluster)

	if len(errs) != 1 {
		t.Fatalf("unexpected errors: %v", errs)
	}
	if errs[0] != ErrEmptyCluster {
		t.Errorf("unexpected error: %v", errs[0])
	}
}

func TestValidateMissingCAFileClusterInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["missing ca"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: "missing",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read certificate-authority"},
	}

	test.testCluster("missing ca", t)
	test.testConfig(t)
}
func TestValidateCleanClusterInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["clean"] = &clientcmdapi.Cluster{
		Server: "anything",
	}
	test := configValidationTest{
		config: config,
	}

	test.testCluster("clean", t)
	test.testConfig(t)
}
func TestValidateCleanWithCAClusterInfo(t *testing.T) {
	tempFile, _ := ioutil.TempFile("", "")
	defer os.Remove(tempFile.Name())

	config := clientcmdapi.NewConfig()
	config.Clusters["clean"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: tempFile.Name(),
	}
	test := configValidationTest{
		config: config,
	}

	test.testCluster("clean", t)
	test.testConfig(t)
}

func TestValidateEmptyAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{}
	test := configValidationTest{
		config: config,
	}

	test.testAuthInfo("error", t)
	test.testConfig(t)
}
func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		ClientCertificate: "missing",
		ClientKey:         "missing",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read client-cert", "unable to read client-key"},
	}

	test.testAuthInfo("error", t)
	test.testConfig(t)
}
func TestValidateCertDataOverridesFiles(t *testing.T) {
	tempFile, _ := ioutil.TempFile("", "")
	defer os.Remove(tempFile.Name())

	config := clientcmdapi.NewConfig()
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		ClientCertificate:     tempFile.Name(),
		ClientCertificateData: []byte("certdata"),
		ClientKey:             tempFile.Name(),
		ClientKeyData:         []byte("keydata"),
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"client-cert-data and client-cert are both specified", "client-key-data and client-key are both specified"},
	}

	test.testAuthInfo("clean", t)
	test.testConfig(t)
}
func TestValidateCleanCertFilesAuthInfo(t *testing.T) {
	tempFile, _ := ioutil.TempFile("", "")
	defer os.Remove(tempFile.Name())

	config := clientcmdapi.NewConfig()
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		ClientCertificate: tempFile.Name(),
		ClientKey:         tempFile.Name(),
	}
	test := configValidationTest{
		config: config,
	}

	test.testAuthInfo("clean", t)
	test.testConfig(t)
}
func TestValidateCleanTokenAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		Token: "any-value",
	}
	test := configValidationTest{
		config: config,
	}

	test.testAuthInfo("clean", t)
	test.testConfig(t)
}

func TestValidateMultipleMethodsAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		Token:    "token",
		Username: "username",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"more than one authentication method", "token", "basicAuth"},
	}

	test.testAuthInfo("error", t)
	test.testConfig(t)
}

type configValidationTest struct {
	config                 *clientcmdapi.Config
	expectedErrorSubstring []string
}

func (c configValidationTest) testContext(contextName string, t *testing.T) {
	errs := validateContext(contextName, *c.config.Contexts[contextName], *c.config)

	if len(c.expectedErrorSubstring) != 0 {
		if len(errs) == 0 {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		}
		for _, curr := range c.expectedErrorSubstring {
			if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
				t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
			}
		}

	} else {
		if len(errs) != 0 {
			t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
		}
	}
}
func (c configValidationTest) testConfirmUsable(contextName string, t *testing.T) {
	err := ConfirmUsable(*c.config, contextName)

	if len(c.expectedErrorSubstring) != 0 {
		if err == nil {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		} else {
			for _, curr := range c.expectedErrorSubstring {
				if err != nil && !strings.Contains(err.Error(), curr) {
					t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
				}
			}
		}
	} else {
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	}
}
func (c configValidationTest) testConfig(t *testing.T) {
	err := Validate(*c.config)

	if len(c.expectedErrorSubstring) != 0 {
		if err == nil {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		} else {
			for _, curr := range c.expectedErrorSubstring {
				if err != nil && !strings.Contains(err.Error(), curr) {
					t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
				}
			}
			if !IsConfigurationInvalid(err) {
				t.Errorf("all errors should be configuration invalid: %v", err)
			}
		}
	} else {
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	}
}
func (c configValidationTest) testCluster(clusterName string, t *testing.T) {
	errs := validateClusterInfo(clusterName, *c.config.Clusters[clusterName])

	if len(c.expectedErrorSubstring) != 0 {
		if len(errs) == 0 {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		}
		for _, curr := range c.expectedErrorSubstring {
			if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
				t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
			}
		}

	} else {
		if len(errs) != 0 {
			t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
		}
	}
}

func (c configValidationTest) testAuthInfo(authInfoName string, t *testing.T) {
	errs := validateAuthInfo(authInfoName, *c.config.AuthInfos[authInfoName])

	if len(c.expectedErrorSubstring) != 0 {
		if len(errs) == 0 {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		}
		for _, curr := range c.expectedErrorSubstring {
			if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
				t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
			}
		}

	} else {
		if len(errs) != 0 {
			t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
		}
	}
}
<commit_msg>UPSTREAM: 44221: validateClusterInfo: use clientcmdapi.NewCluster()<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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 clientcmd

import (
	"io/ioutil"
	"os"
	"strings"
	"testing"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

func TestConfirmUsableBadInfoButOkConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["missing ca"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: "missing",
	}
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		Username: "anything",
		Token:    "here",
	}
	config.Contexts["dirty"] = &clientcmdapi.Context{
		Cluster:  "missing ca",
		AuthInfo: "error",
	}
	config.Clusters["clean"] = &clientcmdapi.Cluster{
		Server: "anything",
	}
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		Token: "here",
	}
	config.Contexts["clean"] = &clientcmdapi.Context{
		Cluster:  "clean",
		AuthInfo: "clean",
	}

	badValidation := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read certificate-authority"},
	}
	okTest := configValidationTest{
		config: config,
	}

	okTest.testConfirmUsable("clean", t)
	badValidation.testConfig(t)
}
func TestConfirmUsableBadInfoConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["missing ca"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: "missing",
	}
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		Username: "anything",
		Token:    "here",
	}
	config.Contexts["first"] = &clientcmdapi.Context{
		Cluster:  "missing ca",
		AuthInfo: "error",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read certificate-authority"},
	}

	test.testConfirmUsable("first", t)
}
func TestConfirmUsableEmptyConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
	}

	test.testConfirmUsable("", t)
}
func TestConfirmUsableMissingConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
	}

	test.testConfirmUsable("not-here", t)
}
func TestValidateEmptyConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
	}

	test.testConfig(t)
}
func TestValidateMissingCurrentContextConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"context was not found for specified "},
	}

	test.testConfig(t)
}
func TestIsContextNotFound(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"

	err := Validate(*config)
	if !IsContextNotFound(err) {
		t.Errorf("Expected context not found, but got %v", err)
	}
	if !IsConfigurationInvalid(err) {
		t.Errorf("Expected configuration invalid, but got %v", err)
	}
}

func TestIsEmptyConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()

	err := Validate(*config)
	if !IsEmptyConfig(err) {
		t.Errorf("Expected context not found, but got %v", err)
	}
	if !IsConfigurationInvalid(err) {
		t.Errorf("Expected configuration invalid, but got %v", err)
	}
}

func TestIsConfigurationInvalid(t *testing.T) {
	if newErrConfigurationInvalid([]error{}) != nil {
		t.Errorf("unexpected error")
	}
	if newErrConfigurationInvalid([]error{ErrNoContext}) == ErrNoContext {
		t.Errorf("unexpected error")
	}
	if newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext}) == nil {
		t.Errorf("unexpected error")
	}
	if !IsConfigurationInvalid(newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext})) {
		t.Errorf("unexpected error")
	}
}

func TestValidateMissingReferencesConfig(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"
	config.Contexts["anything"] = &clientcmdapi.Context{Cluster: "missing", AuthInfo: "missing"}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"user \"missing\" was not found for context \"anything\"", "cluster \"missing\" was not found for context \"anything\""},
	}

	test.testContext("anything", t)
	test.testConfig(t)
}
func TestValidateEmptyContext(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.CurrentContext = "anything"
	config.Contexts["anything"] = &clientcmdapi.Context{}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"user was not specified for context \"anything\"", "cluster was not specified for context \"anything\""},
	}

	test.testContext("anything", t)
	test.testConfig(t)
}

func TestValidateEmptyClusterInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["empty"] = clientcmdapi.NewCluster()
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"cluster has no server defined"},
	}

	test.testCluster("empty", t)
	test.testConfig(t)
}

func TestValidateClusterInfoErrEmptyCluster(t *testing.T) {
	cluster := clientcmdapi.NewCluster()
	errs := validateClusterInfo("", *cluster)

	if len(errs) != 1 {
		t.Fatalf("unexpected errors: %v", errs)
	}
	if errs[0] != ErrEmptyCluster {
		t.Errorf("unexpected error: %v", errs[0])
	}
}

func TestValidateMissingCAFileClusterInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["missing ca"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: "missing",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read certificate-authority"},
	}

	test.testCluster("missing ca", t)
	test.testConfig(t)
}
func TestValidateCleanClusterInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.Clusters["clean"] = &clientcmdapi.Cluster{
		Server: "anything",
	}
	test := configValidationTest{
		config: config,
	}

	test.testCluster("clean", t)
	test.testConfig(t)
}
func TestValidateCleanWithCAClusterInfo(t *testing.T) {
	tempFile, _ := ioutil.TempFile("", "")
	defer os.Remove(tempFile.Name())

	config := clientcmdapi.NewConfig()
	config.Clusters["clean"] = &clientcmdapi.Cluster{
		Server:               "anything",
		CertificateAuthority: tempFile.Name(),
	}
	test := configValidationTest{
		config: config,
	}

	test.testCluster("clean", t)
	test.testConfig(t)
}

func TestValidateEmptyAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{}
	test := configValidationTest{
		config: config,
	}

	test.testAuthInfo("error", t)
	test.testConfig(t)
}
func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		ClientCertificate: "missing",
		ClientKey:         "missing",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"unable to read client-cert", "unable to read client-key"},
	}

	test.testAuthInfo("error", t)
	test.testConfig(t)
}
func TestValidateCertDataOverridesFiles(t *testing.T) {
	tempFile, _ := ioutil.TempFile("", "")
	defer os.Remove(tempFile.Name())

	config := clientcmdapi.NewConfig()
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		ClientCertificate:     tempFile.Name(),
		ClientCertificateData: []byte("certdata"),
		ClientKey:             tempFile.Name(),
		ClientKeyData:         []byte("keydata"),
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"client-cert-data and client-cert are both specified", "client-key-data and client-key are both specified"},
	}

	test.testAuthInfo("clean", t)
	test.testConfig(t)
}
func TestValidateCleanCertFilesAuthInfo(t *testing.T) {
	tempFile, _ := ioutil.TempFile("", "")
	defer os.Remove(tempFile.Name())

	config := clientcmdapi.NewConfig()
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		ClientCertificate: tempFile.Name(),
		ClientKey:         tempFile.Name(),
	}
	test := configValidationTest{
		config: config,
	}

	test.testAuthInfo("clean", t)
	test.testConfig(t)
}
func TestValidateCleanTokenAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
		Token: "any-value",
	}
	test := configValidationTest{
		config: config,
	}

	test.testAuthInfo("clean", t)
	test.testConfig(t)
}

func TestValidateMultipleMethodsAuthInfo(t *testing.T) {
	config := clientcmdapi.NewConfig()
	config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
		Token:    "token",
		Username: "username",
	}
	test := configValidationTest{
		config:                 config,
		expectedErrorSubstring: []string{"more than one authentication method", "token", "basicAuth"},
	}

	test.testAuthInfo("error", t)
	test.testConfig(t)
}

type configValidationTest struct {
	config                 *clientcmdapi.Config
	expectedErrorSubstring []string
}

func (c configValidationTest) testContext(contextName string, t *testing.T) {
	errs := validateContext(contextName, *c.config.Contexts[contextName], *c.config)

	if len(c.expectedErrorSubstring) != 0 {
		if len(errs) == 0 {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		}
		for _, curr := range c.expectedErrorSubstring {
			if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
				t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
			}
		}

	} else {
		if len(errs) != 0 {
			t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
		}
	}
}
func (c configValidationTest) testConfirmUsable(contextName string, t *testing.T) {
	err := ConfirmUsable(*c.config, contextName)

	if len(c.expectedErrorSubstring) != 0 {
		if err == nil {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		} else {
			for _, curr := range c.expectedErrorSubstring {
				if err != nil && !strings.Contains(err.Error(), curr) {
					t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
				}
			}
		}
	} else {
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	}
}
func (c configValidationTest) testConfig(t *testing.T) {
	err := Validate(*c.config)

	if len(c.expectedErrorSubstring) != 0 {
		if err == nil {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		} else {
			for _, curr := range c.expectedErrorSubstring {
				if err != nil && !strings.Contains(err.Error(), curr) {
					t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
				}
			}
			if !IsConfigurationInvalid(err) {
				t.Errorf("all errors should be configuration invalid: %v", err)
			}
		}
	} else {
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	}
}
func (c configValidationTest) testCluster(clusterName string, t *testing.T) {
	errs := validateClusterInfo(clusterName, *c.config.Clusters[clusterName])

	if len(c.expectedErrorSubstring) != 0 {
		if len(errs) == 0 {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		}
		for _, curr := range c.expectedErrorSubstring {
			if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
				t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
			}
		}

	} else {
		if len(errs) != 0 {
			t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
		}
	}
}

func (c configValidationTest) testAuthInfo(authInfoName string, t *testing.T) {
	errs := validateAuthInfo(authInfoName, *c.config.AuthInfos[authInfoName])

	if len(c.expectedErrorSubstring) != 0 {
		if len(errs) == 0 {
			t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
		}
		for _, curr := range c.expectedErrorSubstring {
			if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
				t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
			}
		}

	} else {
		if len(errs) != 0 {
			t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
		}
	}
}
<|endoftext|>


<commit_before>package jpholiday

import (
	"fmt"
	"time"
)

// 祝日表現
type NamedHoliday int

const (
	GANTAN       NamedHoliday = iota
	SEIJIN                    = iota
	KENKOKUKINEN              = iota
	SHUNBUN                   = iota
	SHOWA                     = iota
	KENPOKINEN                = iota
	MIDORI                    = iota
	KODOMO                    = iota
	UMI                       = iota
	KEIRO                     = iota
	SHUBUN                    = iota
	TAIIKU                    = iota
	BUNKA                     = iota
	KINROKANSHA               = iota
	TENNOTANJOBI              = iota

	//振替休日
	FURIKAEKYUJITSU NamedHoliday = iota
	//国民の休日
	KOKUMINNOKYUJITSU NamedHoliday = iota
)

// 祝日の名前マップ
var (
	HOLIDAY_NAMES = map[NamedHoliday]string{
		GANTAN:            "元旦",
		SEIJIN:            "成人の日",
		KENKOKUKINEN:      "建国記念の日",
		SHUNBUN:           "春分の日",
		SHOWA:             "昭和の日",
		KENPOKINEN:        "憲法記念日",
		MIDORI:            "みどりの日",
		KODOMO:            "こどもの日",
		UMI:               "海の日",
		KEIRO:             "敬老の日",
		SHUBUN:            "秋分の日",
		TAIIKU:            "体育の日",
		BUNKA:             "文化の日",
		KINROKANSHA:       "勤労感謝の日",
		TENNOTANJOBI:      "天皇誕生日",
		FURIKAEKYUJITSU:   "振替休日",
		KOKUMINNOKYUJITSU: "国民の休日",
	}
)

func (h NamedHoliday) String() string {
	return HOLIDAY_NAMES[h]
}

//第○×曜日で判定する関数を作成するファクトリ関数
//ハッピーマンデーなどに使用。
func DynamicHolidayCheckerFactory(month time.Month, nth int, weekday time.Weekday) (f func(Date) bool) {
	f = func(d Date) bool {
		if month == d.Month() && weekday == d.Weekday() && nth == d.NthWeekday() {
			return true
		}
		return false
	}
	return
}

// 何月何日で判定する関数を作成するファクトリ関数
// 毎年同じ日付の祝日に使用
func StaticHolidayCheckerFactory(month time.Month, day int) (f func(Date) bool) {
	f = func(d Date) bool {
		if month == d.Month() && day == d.Day() {
			return true
		}
		return false
	}
	return
}

var (
	LOCATION_JP, _ = time.LoadLocation("Asia/Tokyo") // Japan Locale
	DHCF           = DynamicHolidayCheckerFactory    // Alias
	SHCF           = StaticHolidayCheckerFactory     // Alias
)

var (
	// 祝日チェッカー関数のmap
	NAMED_HOLIDAYS = map[NamedHoliday]func(Date) bool{
		GANTAN:       SHCF(1, 1),
		SEIJIN:       DHCF(1, 2, time.Monday),
		KENKOKUKINEN: SHCF(2, 11),
		SHUNBUN:      ShunbunCheker,
		SHOWA:        SHCF(4, 29),
		KENPOKINEN:   SHCF(5, 3),
		MIDORI:       SHCF(5, 4),
		KODOMO:       SHCF(5, 5),
		UMI:          DHCF(7, 3, time.Monday),
		KEIRO:        DHCF(9, 3, time.Monday),
		SHUBUN:       ShubunCheker,
		TAIIKU:       DHCF(10, 2, time.Monday),
		BUNKA:        SHCF(11, 3),
		KINROKANSHA:  SHCF(11, 23),
		TENNOTANJOBI: SHCF(12, 23),
	}
)

func init() {
	//春分の日のリストを初期化
	for i, day := range SHUNBUN_DAYS {
		year := i + 2000
		SHUNBUN_LIST[NewDate(year, 3, day)] = struct{}{}
	}

	//秋分の日のリストを初期化
	for i, day := range SHUBUN_DAYS {
		year := i + 2000
		SHUBUN_LIST[NewDate(year, 9, day)] = struct{}{}
	}

}

// 祝日判定用日付構造体。
// 時間以下のデータは無視する。
// NewDate, TimeToDateでオブジェクト作成することで時間以下のデータをzero-fillして作る。
// Date{time.Time}で作成しないこと。
type Date struct {
	time.Time
}

// 年、月、日からDateを生成する
func NewDate(year int, month time.Month, day int) (d Date) {
	d = Date{time.Date(year, month, day, 0, 0, 0, 0, LOCATION_JP)}
	return
}

// time.TimeからDateを生成する
func TimeToDate(t time.Time) (d Date) {
	tmp := t.In(LOCATION_JP)
	d = NewDate(tmp.Year(), tmp.Month(), tmp.Day())
	return
}

// time.Timeに変換
func (d Date) ToTime() time.Time {
	return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, LOCATION_JP)
}

// その曜日がその月で何回目かを返す
func (d Date) NthWeekday() (nth int) {
	day := d.Day()
	nth = ((day - 1) / 7) + 1
	return
}

// 国民の祝日ならtrueと祝日名を返す。振替休日はチェックしない。
func (d Date) RealHoliday() (isHoliday bool, holiday NamedHoliday) {
	for holiday, f := range NAMED_HOLIDAYS {
		if f(d) {
			if holiday == MIDORI && d.Year() < 2007 {
				return false, -1
			}
			return true, holiday
		}
	}
	return false, -1
}

// 振り替え休日ならtrue otherwise false
func (d Date) AlternativeHoliday() (isHoliday bool) {
	yesterday := d.Yesterday()
	for {
		y, _ := yesterday.RealHoliday()
		if y {
			if yesterday.Weekday() == time.Sunday {
				return true
			}
		} else {
			return false
		}
		yesterday = yesterday.Yesterday()
	}
	return false
}

// 国民の休日ならtrue otheriwise false
func (d Date) IsSandwitched() (isHoliday bool) {
	if dok, _ := d.RealHoliday(); dok {
		return false
	}
	yesterday := d.Yesterday()
	tommorow := d.Tommorow()
	yok, _ := yesterday.RealHoliday()
	tok, _ := tommorow.RealHoliday()
	if yok && tok {
		isHoliday = true
	} else {
		isHoliday = false
	}
	return
}

// 祝日ならtrueと祝日名を返す。
// 振替休日の祝日名は"振替休日"
// 国民の休日の祝日名は"国民の休日"
func (d Date) Holiday() (isHoliday bool, holiday NamedHoliday) {
	if isHoliday, holiday = d.RealHoliday(); isHoliday {
		return
	} else {
		if d.AlternativeHoliday() {
			return true, FURIKAEKYUJITSU
		} else if d.IsSandwitched() {
			return true, KOKUMINNOKYUJITSU
		}
	}
	return false, -1
}

// 1日前のDateを返す
func (d Date) Yesterday() Date {
	return NewDate(d.Year(), d.Month(), d.Day()-1)
}

// 1日後のDateを返す
func (d Date) Tommorow() Date {
	return NewDate(d.Year(), d.Month(), d.Day()+1)
}

// Dateの同一性判定
func (d Date) Equal(another Date) bool {
	if d.Year() == another.Year() && d.Month() == another.Month() && d.Day() == another.Day() {
		return true
	}
	return false
}

func (d Date) String() string {
	return fmt.Sprintf("%04d-%02d-%02d", d.Year(), d.Month(), d.Day())
}
<commit_msg>ドキュメント修正<commit_after>package jpholiday

import (
	"fmt"
	"time"
)

// 祝日表現
type NamedHoliday int

const (
	GANTAN       NamedHoliday = iota
	SEIJIN                    = iota
	KENKOKUKINEN              = iota
	SHUNBUN                   = iota
	SHOWA                     = iota
	KENPOKINEN                = iota
	MIDORI                    = iota
	KODOMO                    = iota
	UMI                       = iota
	KEIRO                     = iota
	SHUBUN                    = iota
	TAIIKU                    = iota
	BUNKA                     = iota
	KINROKANSHA               = iota
	TENNOTANJOBI              = iota

	//振替休日
	FURIKAEKYUJITSU NamedHoliday = iota
	//国民の休日
	KOKUMINNOKYUJITSU NamedHoliday = iota
)

// 祝日の名前マップ
var (
	HOLIDAY_NAMES = map[NamedHoliday]string{
		GANTAN:            "元旦",
		SEIJIN:            "成人の日",
		KENKOKUKINEN:      "建国記念の日",
		SHUNBUN:           "春分の日",
		SHOWA:             "昭和の日",
		KENPOKINEN:        "憲法記念日",
		MIDORI:            "みどりの日",
		KODOMO:            "こどもの日",
		UMI:               "海の日",
		KEIRO:             "敬老の日",
		SHUBUN:            "秋分の日",
		TAIIKU:            "体育の日",
		BUNKA:             "文化の日",
		KINROKANSHA:       "勤労感謝の日",
		TENNOTANJOBI:      "天皇誕生日",
		FURIKAEKYUJITSU:   "振替休日",
		KOKUMINNOKYUJITSU: "国民の休日",
	}
)

func (h NamedHoliday) String() string {
	return HOLIDAY_NAMES[h]
}

//第○×曜日で判定する関数を作成するファクトリ関数
//ハッピーマンデーなどに使用。
func DynamicHolidayCheckerFactory(month time.Month, nth int, weekday time.Weekday) (f func(Date) bool) {
	f = func(d Date) bool {
		if month == d.Month() && weekday == d.Weekday() && nth == d.NthWeekday() {
			return true
		}
		return false
	}
	return
}

// 何月何日で判定する関数を作成するファクトリ関数
// 毎年同じ日付の祝日に使用
func StaticHolidayCheckerFactory(month time.Month, day int) (f func(Date) bool) {
	f = func(d Date) bool {
		if month == d.Month() && day == d.Day() {
			return true
		}
		return false
	}
	return
}

var (
	LOCATION_JP, _ = time.LoadLocation("Asia/Tokyo") // Japan Locale
	DHCF           = DynamicHolidayCheckerFactory    // Alias
	SHCF           = StaticHolidayCheckerFactory     // Alias
)

var (
	// 祝日チェッカー関数のmap
	NAMED_HOLIDAYS = map[NamedHoliday]func(Date) bool{
		GANTAN:       SHCF(1, 1),
		SEIJIN:       DHCF(1, 2, time.Monday),
		KENKOKUKINEN: SHCF(2, 11),
		SHUNBUN:      ShunbunCheker,
		SHOWA:        SHCF(4, 29),
		KENPOKINEN:   SHCF(5, 3),
		MIDORI:       SHCF(5, 4),
		KODOMO:       SHCF(5, 5),
		UMI:          DHCF(7, 3, time.Monday),
		KEIRO:        DHCF(9, 3, time.Monday),
		SHUBUN:       ShubunCheker,
		TAIIKU:       DHCF(10, 2, time.Monday),
		BUNKA:        SHCF(11, 3),
		KINROKANSHA:  SHCF(11, 23),
		TENNOTANJOBI: SHCF(12, 23),
	}
)

func init() {
	//春分の日のリストを初期化
	for i, day := range SHUNBUN_DAYS {
		year := i + 2000
		SHUNBUN_LIST[NewDate(year, 3, day)] = struct{}{}
	}

	//秋分の日のリストを初期化
	for i, day := range SHUBUN_DAYS {
		year := i + 2000
		SHUBUN_LIST[NewDate(year, 9, day)] = struct{}{}
	}

}

// 祝日判定用日付構造体。
// 時間以下のデータは無視する。
// NewDate, TimeToDateでオブジェクト作成することで時間以下のデータをzero-fillして作る。
// Date{time.Time}で作成しないこと。
type Date struct {
	time.Time
}

// 年、月、日からDateを生成する
func NewDate(year int, month time.Month, day int) (d Date) {
	d = Date{time.Date(year, month, day, 0, 0, 0, 0, LOCATION_JP)}
	return
}

// time.TimeからDateを生成する
func TimeToDate(t time.Time) (d Date) {
	tmp := t.In(LOCATION_JP)
	d = NewDate(tmp.Year(), tmp.Month(), tmp.Day())
	return
}

// time.Timeに変換
func (d Date) ToTime() time.Time {
	return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, LOCATION_JP)
}

// その曜日がその月で何回目かを返す
func (d Date) NthWeekday() (nth int) {
	day := d.Day()
	nth = ((day - 1) / 7) + 1
	return
}

// 国民の祝日ならtrueと祝日を返す。
// 振替休日と国民の休日はチェックしない。
func (d Date) RealHoliday() (isHoliday bool, holiday NamedHoliday) {
	for holiday, f := range NAMED_HOLIDAYS {
		if f(d) {
			if holiday == MIDORI && d.Year() < 2007 {
				return false, -1
			}
			return true, holiday
		}
	}
	return false, -1
}

// 振り替え休日ならtrue otherwise false
func (d Date) AlternativeHoliday() (isHoliday bool) {
	yesterday := d.Yesterday()
	for {
		y, _ := yesterday.RealHoliday()
		if y {
			if yesterday.Weekday() == time.Sunday {
				return true
			}
		} else {
			return false
		}
		yesterday = yesterday.Yesterday()
	}
	return false
}

// 国民の休日ならtrue otheriwise false
func (d Date) IsSandwitched() (isHoliday bool) {
	if dok, _ := d.RealHoliday(); dok {
		return false
	}
	yesterday := d.Yesterday()
	tommorow := d.Tommorow()
	yok, _ := yesterday.RealHoliday()
	tok, _ := tommorow.RealHoliday()
	if yok && tok {
		isHoliday = true
	} else {
		isHoliday = false
	}
	return
}

// 祝日ならtrueと祝日を返す。
// 振替休日と国民の休日もチェックする
func (d Date) Holiday() (isHoliday bool, holiday NamedHoliday) {
	if isHoliday, holiday = d.RealHoliday(); isHoliday {
		return
	} else {
		if d.AlternativeHoliday() {
			return true, FURIKAEKYUJITSU
		} else if d.IsSandwitched() {
			return true, KOKUMINNOKYUJITSU
		}
	}
	return false, -1
}

// 1日前のDateを返す
func (d Date) Yesterday() Date {
	return NewDate(d.Year(), d.Month(), d.Day()-1)
}

// 1日後のDateを返す
func (d Date) Tommorow() Date {
	return NewDate(d.Year(), d.Month(), d.Day()+1)
}

// Dateの同一性判定
func (d Date) Equal(another Date) bool {
	if d.Year() == another.Year() && d.Month() == another.Month() && d.Day() == another.Day() {
		return true
	}
	return false
}

func (d Date) String() string {
	return fmt.Sprintf("%04d-%02d-%02d", d.Year(), d.Month(), d.Day())
}
<|endoftext|>


<commit_before>// Copyright 2015 Yoshi Yamaguchi
//
// 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 shortener

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"path"
	"time"

	"appengine"
	"appengine/datastore"
)

const (
	Version            = "0.1.0"
	starttime          = int64(1437625938157481) // around 2015 Jul 23 13:34
	timestampLeftShift = 22
)

var chars []byte // used for unique id generation.

// initChars initialize chars as sequence of 0-9A-Za-z
func initChars() {
	chars = make([]byte, 62)
	for i := 0; i < 10; i++ { // 0-9
		chars[i] = byte(48 + i)
	}
	for i := 0; i < 26; i++ { // A-Z
		chars[i+10] = byte(65 + i)
	}
	for i := 0; i < 26; i++ { // a-z
		chars[i+36] = byte(97 + i)
	}
}

// init setup chars and URL routers.
func init() {
	initChars()
	router := &RegexpHandler{}
	router.HandleFunc(`/`, top)
	router.HandleFunc(`/[0-9A-Za-z_\-]{10,}`, redirect)
	router.HandleFunc(`/version`, version)
	router.HandleFunc(`/shortener/v1`, shortener)
	http.Handle("/", router)
}

// version returns application version.
func version(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Version)
}

// top returns UI front page.
func top(w http.ResponseWriter, r *http.Request) {

	fmt.Fprintf(w, "hello")
}

// shortener
func shortener(w http.ResponseWriter, r *http.Request) {
	req := URLRequest{}
	if r.Method != "POST" {
		http.Error(w, fmt.Sprintf("Methods but for POST are not allowed"), http.StatusMethodNotAllowed)
		return
	}
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, fmt.Sprintf("Unexpected payload: %v", err), http.StatusInternalServerError)
		return
	}
	err = json.Unmarshal(data, &req)
	if err != nil {
		http.Error(w, fmt.Sprintf("JSON decode error: %v", err), http.StatusInternalServerError)
		return
	}

	url, err := url.Parse(req.URL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Invalid URL: %v", req.URL), http.StatusInternalServerError)
		return
	}
	switch url.Scheme {
	case "https", "http", "ftp":
		break
	default:
		http.Error(w, fmt.Sprintf("Scheme is not supported: %v", req.URL), http.StatusInternalServerError)
		return
	}

	c := appengine.NewContext(r)
	id := uniqueid()
	e := &URLEntity{
		ID:  id,
		URL: req.URL,
	}
	key := datastore.NewIncompleteKey(c, "URL", nil)
	_, err = datastore.Put(c, key, e)
	if err != nil {
		http.Error(w, fmt.Sprintf("datastore put error: %v", err), http.StatusInternalServerError)
		return
	}
	entry, err := json.Marshal(e)
	if err != nil {
		http.Error(w, fmt.Sprintf("JSON encode error: %v", err), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "%v", string(entry))
}

// redirect find specified shortened URL path from datastore and redirect to original URL.
func redirect(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	id := path.Base(r.URL.Path)
	es := []URLEntity{}
	keys, err := datastore.NewQuery("URL").Filter("ID=", id).GetAll(c, &es)
	if err != nil {
		http.Error(w, fmt.Sprintf("datastore get error: %v", err), http.StatusInternalServerError)
		return
	}
	if len(es) == 0 {
		http.Redirect(w, r, "/", http.StatusNotFound)
		return
	}
	original := es[0].URL
	es[0].Count += 1
	_, err = datastore.Put(c, keys[0], &es[0])
	if err != nil {
		http.Error(w, fmt.Sprintf("datastore put error: %v", err), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, original, http.StatusFound)
}

// uniqueid generates unique id from unix time in microsecond and randomnumber based on it,
// then convert it to base 62 number
func uniqueid() string {
	now := time.Now().UnixNano() / 1000
	delta := now - starttime
	rand.Seed(delta)
	n := rand.Intn(2 ^ timestampLeftShift)
	id := delta<<timestampLeftShift | int64(n)

	size := int64(len(chars))
	result := make([]byte, 36)
	i := 0
	for id > 0 {
		rem := id % size
		id = id / size
		result[i] = chars[rem]
		i++
	}
	return string(result[:i])
}
<commit_msg>fix typo<commit_after>// Copyright 2015 Yoshi Yamaguchi
//
// 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 shortener

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"path"
	"time"

	"appengine"
	"appengine/datastore"
)

const (
	Version            = "0.1.0"
	starttime          = int64(1437625938157481) // around 2015 Jul 23 13:34
	timestampLeftShift = 22
)

var chars []byte // used for unique id generation.

// initChars initialize chars as sequence of 0-9A-Za-z
func initChars() {
	chars = make([]byte, 62)
	for i := 0; i < 10; i++ { // 0-9
		chars[i] = byte(48 + i)
	}
	for i := 0; i < 26; i++ { // A-Z
		chars[i+10] = byte(65 + i)
	}
	for i := 0; i < 26; i++ { // a-z
		chars[i+36] = byte(97 + i)
	}
}

// init setup chars and URL routers.
func init() {
	initChars()
	router := &RegexpHandler{}
	router.HandleFunc(`/`, top)
	router.HandleFunc(`/[0-9A-Za-z_\-]{10,}`, redirect)
	router.HandleFunc(`/version`, version)
	router.HandleFunc(`/shortener/v1`, shortener)
	http.Handle("/", router)
}

// version returns application version.
func version(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, Version)
}

// top returns UI front page.
func top(w http.ResponseWriter, r *http.Request) {

	fmt.Fprintf(w, "hello")
}

// shortener
func shortener(w http.ResponseWriter, r *http.Request) {
	req := URLRequest{}
	if r.Method != "POST" {
		http.Error(w, fmt.Sprintf("Methods but for POST are not allowed"), http.StatusMethodNotAllowed)
		return
	}
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, fmt.Sprintf("Unexpected payload: %v", err), http.StatusInternalServerError)
		return
	}
	err = json.Unmarshal(data, &req)
	if err != nil {
		http.Error(w, fmt.Sprintf("JSON decode error: %v", err), http.StatusInternalServerError)
		return
	}
	url, err := url.Parse(req.URL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Invalid URL: %v", req.URL), http.StatusInternalServerError)
		return
	}
	switch url.Scheme {
	case "https", "http", "ftp":
		break
	default:
		http.Error(w, fmt.Sprintf("Scheme is not supported: %v", req.URL), http.StatusInternalServerError)
		return
	}

	c := appengine.NewContext(r)
	id := uniqueid()
	e := &URLEntity{
		ID:  id,
		URL: req.URL,
	}
	key := datastore.NewIncompleteKey(c, "URL", nil)
	_, err = datastore.Put(c, key, e)
	if err != nil {
		http.Error(w, fmt.Sprintf("datastore put error: %v", err), http.StatusInternalServerError)
		return
	}
	entry, err := json.Marshal(e)
	if err != nil {
		http.Error(w, fmt.Sprintf("JSON encode error: %v", err), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "%v", string(entry))
}

// redirect find specified shortened URL path from datastore and redirect to original URL.
func redirect(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	id := path.Base(r.URL.Path)
	es := []URLEntity{}
	keys, err := datastore.NewQuery("URL").Filter("ID=", id).GetAll(c, &es)
	if err != nil {
		http.Error(w, fmt.Sprintf("datastore get error: %v", err), http.StatusInternalServerError)
		return
	}
	if len(es) == 0 {
		http.Redirect(w, r, "/", http.StatusNotFound)
		return
	}
	original := es[0].URL
	es[0].Count += 1
	_, err = datastore.Put(c, keys[0], &es[0])
	if err != nil {
		http.Error(w, fmt.Sprintf("datastore put error: %v", err), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, original, http.StatusFound)
}

// uniqueid generates unique id from unix time in microsecond and randomnumber based on it,
// then convert it to base 62 number
func uniqueid() string {
	now := time.Now().UnixNano() / 1000
	delta := now - starttime
	rand.Seed(delta)
	n := rand.Intn(2 ^ timestampLeftShift)
	id := delta<<timestampLeftShift | int64(n)

	size := int64(len(chars))
	result := make([]byte, 36)
	i := 0
	for id > 0 {
		rem := id % size
		id = id / size
		result[i] = chars[rem]
		i++
	}
	return string(result[:i])
}
<|endoftext|>


<commit_before>package elements

import (
	"encoding/csv"
	"github.com/faiface/pixel"
	"github.com/faiface/pixel/pixelgl"
	"image/color"
	"io"
	"os"
	"strconv"
	"fmt"
)

type World struct {
	objects []*Object
	enemies []*Enemy
	doors   []*Door
	bgColor color.Color
	LinkPos pixel.Vec
}

const (
	CURRENT = iota + 1
	OVERWORLD
	CAVE
	CASTLE
)

func CreateWorld(worldType int) *World {
	if worldType == OVERWORLD {
		return readWorld("elements/overworld.csv")
	} else if worldType == CAVE {
		return readWorld("elements/cave.csv")
	} else if worldType == CASTLE {
		return readWorld("elements/castle.csv")
	}

	return new(World)
}

func (world *World) UpdateAndDraw(win *pixelgl.Window) {
	win.Clear(world.bgColor)

	for _, o := range world.objects {
		o.draw(win)
	}

	for _, d := range world.doors {
		d.draw(win)
	}

	for _, e := range world.enemies {
		if !e.isDead {
			e.update(win, world.objects, world.enemies)
			e.draw(win)
		}
	}

}

func readWorld(path string) *World {
	world := new(World)
	var objects []*Object
	var enemies []*Enemy
	var doors []*Door
	world.LinkPos = pixel.V(0, 0)

	descFile, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer descFile.Close()

	desc := csv.NewReader(descFile)
	desc.FieldsPerRecord = -1
	for {
		element, err := desc.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil
		}

		switch element[0] {
		case "object":
			x, _ := strconv.Atoi(element[2])
			y, _ := strconv.Atoi(element[3])
			blocking, _ := strconv.ParseBool(element[4])
			objects = append(objects, NewObject(element[1], pixel.V(float64(x), float64(y)), blocking))
			fmt.Println(pixel.V(float64(x), float64(y)))
		case "enemy":
			x, _ := strconv.Atoi(element[2])
			y, _ := strconv.Atoi(element[3])
			enemies = append(enemies, NewEnemy(pixel.V(float64(x), float64(y)), element[1]))
		case "door":
			x, _ := strconv.Atoi(element[2])
			y, _ := strconv.Atoi(element[3])
			target, _ := strconv.Atoi(element[4])
			doors = append(doors, NewDoor(element[1], pixel.V(float64(x), float64(y)), target))
		case "linkPos":
			x, _ := strconv.Atoi(element[1])
			y, _ := strconv.Atoi(element[2])
			world.LinkPos = pixel.V(float64(x), float64(y))
		case "bgColor":
			r, _ := strconv.Atoi(element[1])
			g, _ := strconv.Atoi(element[2])
			b, _ := strconv.Atoi(element[3])
			world.bgColor = color.RGBA{uint8(r), uint8(g), uint8(b), 1}
		}
	}

	world.objects = objects
	world.enemies = enemies
	world.doors = doors
	return world

}
<commit_msg>Removing debug statements<commit_after>package elements

import (
	"encoding/csv"
	"github.com/faiface/pixel"
	"github.com/faiface/pixel/pixelgl"
	"image/color"
	"io"
	"os"
	"strconv"
)

type World struct {
	objects []*Object
	enemies []*Enemy
	doors   []*Door
	bgColor color.Color
	LinkPos pixel.Vec
}

const (
	CURRENT = iota + 1
	OVERWORLD
	CAVE
	CASTLE
)

func CreateWorld(worldType int) *World {
	if worldType == OVERWORLD {
		return readWorld("elements/overworld.csv")
	} else if worldType == CAVE {
		return readWorld("elements/cave.csv")
	} else if worldType == CASTLE {
		return readWorld("elements/castle.csv")
	}

	return new(World)
}

func (world *World) UpdateAndDraw(win *pixelgl.Window) {
	win.Clear(world.bgColor)

	for _, o := range world.objects {
		o.draw(win)
	}

	for _, d := range world.doors {
		d.draw(win)
	}

	for _, e := range world.enemies {
		if !e.isDead {
			e.update(win, world.objects, world.enemies)
			e.draw(win)
		}
	}

}

func readWorld(path string) *World {
	world := new(World)
	var objects []*Object
	var enemies []*Enemy
	var doors []*Door
	world.LinkPos = pixel.V(0, 0)

	descFile, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer descFile.Close()

	desc := csv.NewReader(descFile)
	desc.FieldsPerRecord = -1
	for {
		element, err := desc.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil
		}

		switch element[0] {
		case "object":
			x, _ := strconv.Atoi(element[2])
			y, _ := strconv.Atoi(element[3])
			blocking, _ := strconv.ParseBool(element[4])
			objects = append(objects, NewObject(element[1], pixel.V(float64(x), float64(y)), blocking))
		case "enemy":
			x, _ := strconv.Atoi(element[2])
			y, _ := strconv.Atoi(element[3])
			enemies = append(enemies, NewEnemy(pixel.V(float64(x), float64(y)), element[1]))
		case "door":
			x, _ := strconv.Atoi(element[2])
			y, _ := strconv.Atoi(element[3])
			target, _ := strconv.Atoi(element[4])
			doors = append(doors, NewDoor(element[1], pixel.V(float64(x), float64(y)), target))
		case "linkPos":
			x, _ := strconv.Atoi(element[1])
			y, _ := strconv.Atoi(element[2])
			world.LinkPos = pixel.V(float64(x), float64(y))
		case "bgColor":
			r, _ := strconv.Atoi(element[1])
			g, _ := strconv.Atoi(element[2])
			b, _ := strconv.Atoi(element[3])
			world.bgColor = color.RGBA{uint8(r), uint8(g), uint8(b), 1}
		}
	}

	world.objects = objects
	world.enemies = enemies
	world.doors = doors
	return world

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/build"
)

var (
	// globalConfig is used by the cobra package to fill out the configuration
	// variables.
	globalConfig Config
)

// exit codes
// inspired by sysexits.h
const (
	exitCodeGeneral = 1  // Not in sysexits.h, but is standard practice.
	exitCodeUsage   = 64 // EX_USAGE in sysexits.h
)

// The Config struct contains all configurable variables for siad. It is
// compatible with gcfg.
type Config struct {
	// The APIPassword is input by the user after the daemon starts up, if the
	// --authenticate-api flag is set.
	APIPassword string

	// The Siad variables are referenced directly by cobra, and are set
	// according to the flags.
	Siad struct {
		APIaddr      string
		RPCaddr      string
		HostAddr     string
		AllowAPIBind bool

		Modules           string
		NoBootstrap       bool
		RequiredUserAgent string
		AuthenticateAPI   bool

		Profile    bool
		ProfileDir string
		SiaDir     string
	}
}

// die prints its arguments to stderr, then exits the program with the default
// error code.
func die(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(exitCodeGeneral)
}

// versionCmd is a cobra command that prints the version of siad.
func versionCmd(*cobra.Command, []string) {
	switch build.Release {
	case "dev":
		fmt.Println("Sia Daemon v" + build.Version + "-dev")
	case "standard":
		fmt.Println("Sia Daemon v" + build.Version)
	case "testing":
		fmt.Println("Sia Daemon v" + build.Version + "-testing")
	default:
		fmt.Println("Sia Daemon v" + build.Version + "-???")
	}
}

// modulesCmd is a cobra command that prints help info about modules.
func modulesCmd(*cobra.Command, []string) {
	fmt.Println(`Use the -M or --modules flag to only run specific modules. Modules are
independent components of Sia. This flag should only be used by developers or
people who want to reduce overhead from unused modules. Modules are specified by
their first letter. If the -M or --modules flag is not specified the default
modules are run. The default modules are:
	gateway, consensus set, host, miner, renter, transaction pool, wallet
This is equivalent to:
	siad -M cghmrtw
Below is a list of all the modules available.

Gateway (g):
	The gateway maintains a peer to peer connection to the network and
	enables other modules to perform RPC calls on peers.
	The gateway is required by all other modules.
	Example:
		siad -M g
Consensus Set (c):
	The consensus set manages everything related to consensus and keeps the
	blockchain in sync with the rest of the network.
	The consensus set requires the gateway.
	Example:
		siad -M gc
Transaction Pool (t):
	The transaction pool manages unconfirmed transactions.
	The transaction pool requires the consensus set.
	Example:
		siad -M gct
Wallet (w):
	The wallet stores and manages siacoins and siafunds.
	The wallet requires the consensus set and transaction pool.
	Example:
		siad -M gctw
Renter (r):
	The renter manages the user's files on the network.
	The renter requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwr
Host (h):
	The host provides storage from local disks to the network. The host
	negotiates file contracts with remote renters to earn money for storing
	other users' files.
	The host requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwh
Miner (m):
	The miner provides a basic CPU mining implementation as well as an API
	for external miners to use.
	The miner requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwm
Explorer (e):
	The explorer provides statistics about the blockchain and can be
	queried for information about specific transactions or other objects on
	the blockchain.
	The explorer requires the consenus set.
	Example:
		siad -M gce`)
}

// main establishes a set of commands and flags using the cobra package.
func main() {
	root := &cobra.Command{
		Use:   os.Args[0],
		Short: "Sia Daemon v" + build.Version,
		Long:  "Sia Daemon v" + build.Version,
		Run:   startDaemonCmd,
	}

	root.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Print version information",
		Long:  "Print version information about the Sia Daemon",
		Run:   versionCmd,
	})

	root.AddCommand(&cobra.Command{
		Use:   "modules",
		Short: "List available modules for use with -M, --modules flag",
		Long:  "List available modules for use with -M, --modules flag and their uses",
		Run:   modulesCmd,
	})

	// Set default values, which have the lowest priority.
	root.Flags().StringVarP(&globalConfig.Siad.RequiredUserAgent, "agent", "", "Sia-Agent", "required substring for the user agent")
	root.Flags().StringVarP(&globalConfig.Siad.HostAddr, "host-addr", "", ":9982", "which port the host listens on")
	root.Flags().StringVarP(&globalConfig.Siad.ProfileDir, "profile-directory", "", "profiles", "location of the profiling directory")
	root.Flags().StringVarP(&globalConfig.Siad.APIaddr, "api-addr", "", "localhost:9980", "which host:port the API server listens on")
	root.Flags().StringVarP(&globalConfig.Siad.SiaDir, "sia-directory", "d", "", "location of the sia directory")
	root.Flags().BoolVarP(&globalConfig.Siad.NoBootstrap, "no-bootstrap", "", false, "disable bootstrapping on this run")
	root.Flags().BoolVarP(&globalConfig.Siad.Profile, "profile", "", false, "enable profiling")
	root.Flags().StringVarP(&globalConfig.Siad.RPCaddr, "rpc-addr", "", ":9981", "which port the gateway listens on")
	root.Flags().StringVarP(&globalConfig.Siad.Modules, "modules", "M", "cghmrtw", "enabled modules, see 'siad modules' for more info")
	root.Flags().BoolVarP(&globalConfig.Siad.AuthenticateAPI, "authenticate-api", "", false, "enable API password protection")
	root.Flags().BoolVarP(&globalConfig.Siad.AllowAPIBind, "disable-api-security", "", false, "allow siad to listen on a non-localhost address (DANGEROUS)")

	// Parse cmdline flags, overwriting both the default values and the config
	// file values.
	if err := root.Execute(); err != nil {
		// Since no commands return errors (all commands set Command.Run instead of
		// Command.RunE), Command.Execute() should only return an error on an
		// invalid command or flag. Therefore Command.Usage() was called (assuming
		// Command.SilenceUsage is false) and we should exit with exitCodeUsage.
		os.Exit(exitCodeUsage)
	}
}
<commit_msg>extra openeing statement if DEBUG<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/build"
)

var (
	// globalConfig is used by the cobra package to fill out the configuration
	// variables.
	globalConfig Config
)

// exit codes
// inspired by sysexits.h
const (
	exitCodeGeneral = 1  // Not in sysexits.h, but is standard practice.
	exitCodeUsage   = 64 // EX_USAGE in sysexits.h
)

// The Config struct contains all configurable variables for siad. It is
// compatible with gcfg.
type Config struct {
	// The APIPassword is input by the user after the daemon starts up, if the
	// --authenticate-api flag is set.
	APIPassword string

	// The Siad variables are referenced directly by cobra, and are set
	// according to the flags.
	Siad struct {
		APIaddr      string
		RPCaddr      string
		HostAddr     string
		AllowAPIBind bool

		Modules           string
		NoBootstrap       bool
		RequiredUserAgent string
		AuthenticateAPI   bool

		Profile    bool
		ProfileDir string
		SiaDir     string
	}
}

// die prints its arguments to stderr, then exits the program with the default
// error code.
func die(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(exitCodeGeneral)
}

// versionCmd is a cobra command that prints the version of siad.
func versionCmd(*cobra.Command, []string) {
	switch build.Release {
	case "dev":
		fmt.Println("Sia Daemon v" + build.Version + "-dev")
	case "standard":
		fmt.Println("Sia Daemon v" + build.Version)
	case "testing":
		fmt.Println("Sia Daemon v" + build.Version + "-testing")
	default:
		fmt.Println("Sia Daemon v" + build.Version + "-???")
	}
}

// modulesCmd is a cobra command that prints help info about modules.
func modulesCmd(*cobra.Command, []string) {
	fmt.Println(`Use the -M or --modules flag to only run specific modules. Modules are
independent components of Sia. This flag should only be used by developers or
people who want to reduce overhead from unused modules. Modules are specified by
their first letter. If the -M or --modules flag is not specified the default
modules are run. The default modules are:
	gateway, consensus set, host, miner, renter, transaction pool, wallet
This is equivalent to:
	siad -M cghmrtw
Below is a list of all the modules available.

Gateway (g):
	The gateway maintains a peer to peer connection to the network and
	enables other modules to perform RPC calls on peers.
	The gateway is required by all other modules.
	Example:
		siad -M g
Consensus Set (c):
	The consensus set manages everything related to consensus and keeps the
	blockchain in sync with the rest of the network.
	The consensus set requires the gateway.
	Example:
		siad -M gc
Transaction Pool (t):
	The transaction pool manages unconfirmed transactions.
	The transaction pool requires the consensus set.
	Example:
		siad -M gct
Wallet (w):
	The wallet stores and manages siacoins and siafunds.
	The wallet requires the consensus set and transaction pool.
	Example:
		siad -M gctw
Renter (r):
	The renter manages the user's files on the network.
	The renter requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwr
Host (h):
	The host provides storage from local disks to the network. The host
	negotiates file contracts with remote renters to earn money for storing
	other users' files.
	The host requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwh
Miner (m):
	The miner provides a basic CPU mining implementation as well as an API
	for external miners to use.
	The miner requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwm
Explorer (e):
	The explorer provides statistics about the blockchain and can be
	queried for information about specific transactions or other objects on
	the blockchain.
	The explorer requires the consenus set.
	Example:
		siad -M gce`)
}

// main establishes a set of commands and flags using the cobra package.
func main() {
	if build.DEBUG {
		fmt.Println("Running with debugging enabled")
	}
	root := &cobra.Command{
		Use:   os.Args[0],
		Short: "Sia Daemon v" + build.Version,
		Long:  "Sia Daemon v" + build.Version,
		Run:   startDaemonCmd,
	}

	root.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Print version information",
		Long:  "Print version information about the Sia Daemon",
		Run:   versionCmd,
	})

	root.AddCommand(&cobra.Command{
		Use:   "modules",
		Short: "List available modules for use with -M, --modules flag",
		Long:  "List available modules for use with -M, --modules flag and their uses",
		Run:   modulesCmd,
	})

	// Set default values, which have the lowest priority.
	root.Flags().StringVarP(&globalConfig.Siad.RequiredUserAgent, "agent", "", "Sia-Agent", "required substring for the user agent")
	root.Flags().StringVarP(&globalConfig.Siad.HostAddr, "host-addr", "", ":9982", "which port the host listens on")
	root.Flags().StringVarP(&globalConfig.Siad.ProfileDir, "profile-directory", "", "profiles", "location of the profiling directory")
	root.Flags().StringVarP(&globalConfig.Siad.APIaddr, "api-addr", "", "localhost:9980", "which host:port the API server listens on")
	root.Flags().StringVarP(&globalConfig.Siad.SiaDir, "sia-directory", "d", "", "location of the sia directory")
	root.Flags().BoolVarP(&globalConfig.Siad.NoBootstrap, "no-bootstrap", "", false, "disable bootstrapping on this run")
	root.Flags().BoolVarP(&globalConfig.Siad.Profile, "profile", "", false, "enable profiling")
	root.Flags().StringVarP(&globalConfig.Siad.RPCaddr, "rpc-addr", "", ":9981", "which port the gateway listens on")
	root.Flags().StringVarP(&globalConfig.Siad.Modules, "modules", "M", "cghmrtw", "enabled modules, see 'siad modules' for more info")
	root.Flags().BoolVarP(&globalConfig.Siad.AuthenticateAPI, "authenticate-api", "", false, "enable API password protection")
	root.Flags().BoolVarP(&globalConfig.Siad.AllowAPIBind, "disable-api-security", "", false, "allow siad to listen on a non-localhost address (DANGEROUS)")

	// Parse cmdline flags, overwriting both the default values and the config
	// file values.
	if err := root.Execute(); err != nil {
		// Since no commands return errors (all commands set Command.Run instead of
		// Command.RunE), Command.Execute() should only return an error on an
		// invalid command or flag. Therefore Command.Usage() was called (assuming
		// Command.SilenceUsage is false) and we should exit with exitCodeUsage.
		os.Exit(exitCodeUsage)
	}
}
<|endoftext|>


<commit_before>package irckit

import (
	"errors"
	"html"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/nlopes/slack"
)

type SlackInfo struct {
	Token      string
	sc         *slack.Client
	rtm        *slack.RTM
	sinfo      *slack.Info
	susers     map[string]slack.User
	connected  bool
	inprogress bool
	sync.RWMutex
}

func (u *User) loginToSlack() (*slack.Client, error) {
	u.sc = slack.New(u.Token)
	u.rtm = u.sc.NewRTM()
	u.Lock()
	u.susers = make(map[string]slack.User)
	u.Unlock()
	go u.rtm.ManageConnection()
	//time.Sleep(time.Second * 2)
	u.sinfo = u.rtm.GetInfo()
	count := 0
	for u.sinfo == nil {
		time.Sleep(time.Millisecond * 500)
		logger.Debug("still waiting for sinfo")
		u.sinfo = u.rtm.GetInfo()
		count++
		if count == 20 {
			return nil, errors.New("couldn't connect in 10 seconds. Check your credentials")
		}
	}

	// we only know which server we are connecting to when we actually are connected.
	// disconnect if we're not allowed
	if len(u.MmInfo.Cfg.SlackSettings.Restrict) > 0 {
		ok := false
		for _, domain := range u.MmInfo.Cfg.SlackSettings.Restrict {
			if domain == u.sinfo.Team.Domain {
				ok = true
				break
			}
		}
		if !ok {
			u.rtm.Disconnect()
			return nil, errors.New("Not allowed to connect to " + u.sinfo.Team.Domain + " slack")
		}
	}
	go u.handleSlack()
	u.addSlackUsersToChannels()
	u.connected = true
	return u.sc, nil
}

func (u *User) logoutFromSlack() error {
	logger.Debug("calling logout from slack")
	err := u.rtm.Disconnect()
	if err != nil {
		logger.Debug("logoutfrom slack", err)
		return err
	}
	u.Srv.Logout(u)
	u.sc = nil
	logger.Info("logout succeeded")
	u.connected = false
	return nil
}

func (u *User) createSlackUser(slackuser *slack.User) *User {
	if slackuser == nil {
		return nil
	}
	if ghost, ok := u.Srv.HasUser(slackuser.Name); ok {
		return ghost
	}
	ghost := &User{Nick: slackuser.Name, User: slackuser.ID, Real: slackuser.RealName, Host: "host", Roles: "", channels: map[Channel]struct{}{}, DisplayName: slackuser.Profile.DisplayName}
	ghost.MmGhostUser = true
	u.Srv.Add(ghost)
	return ghost
}

func (u *User) addSlackUserToChannel(user *slack.User, channel string, channelId string) {
	if user == nil {
		return
	}
	ghost := u.createSlackUser(user)
	if ghost == nil {
		logger.Warnf("Cannot join %v into %s", user, channel)
		return
	}
	logger.Debugf("adding %s to %s (%s)", ghost.Nick, channel, channelId)
	ch := u.Srv.Channel(channelId)
	logger.Debugf("channel: %#v %#v", ch.String(), ch.ID())
	ch.Join(ghost)
}

func (u *User) addSlackUsersToChannels() {
	srv := u.Srv
	throttle := time.Tick(time.Millisecond * 100)
	logger.Debug("in addUsersToChannels()")
	// add all users, also who are not on channels
	ch := srv.Channel("&users")
	users, _ := u.sc.GetUsers()
	for _, mmuser := range users {
		// do not add our own nick
		if mmuser.ID == u.sinfo.User.ID {
			continue
		}
		u.createSlackUser(&mmuser)
		u.addSlackUserToChannel(&mmuser, "&users", "&users")
		u.Lock()
		u.susers[mmuser.ID] = mmuser
		u.Unlock()
	}
	ch.Join(u)

	channels := make(chan interface{}, 10)
	for i := 0; i < 10; i++ {
		go u.addSlackUserToChannelWorker(channels, throttle)
	}
	groups, _ := u.sc.GetGroups(true)
	mmchannels, _ := u.sc.GetChannels(true)
	for _, mmchannel := range mmchannels {
		if mmchannel.IsMember {
			logger.Debug("Adding channel", mmchannel)
			channels <- mmchannel
		}
	}
	for _, mmchannel := range groups {
		logger.Debug("Adding private channel", mmchannel)
		channels <- mmchannel
	}
	close(channels)
}

func (u *User) addSlackUserToChannelWorker(channels <-chan interface{}, throttle <-chan time.Time) {
	var ID, name string
	for {
		mmchannel, ok := <-channels
		if !ok {
			logger.Debug("Done adding user to channels")
			return
		}
		<-throttle
		switch mmchannel.(type) {
		case slack.Channel:
			ID = mmchannel.(slack.Channel).ID
			name = mmchannel.(slack.Channel).Name
			u.syncSlackChannel(ID, name)
		case slack.Group:
			ID = mmchannel.(slack.Group).ID
			name = mmchannel.(slack.Group).Name
			logger.Debugf("GROUP %#v", mmchannel.(slack.Group))
			u.syncSlackGroup(ID, name)

		}
		// exclude direct messages
		//var spoof func(string, string)
		//ch := u.Srv.Channel(mmchannel.ID)
		// post everything to the channel you haven't seen yet
	}
}

func (u *User) handleSlack() {
	for {
		/*
			if u.mc.WsQuit {
				logger.Debug("exiting handleWsMessage")
				return
			}
		*/
		logger.Debug("in handleSlack")
		for msg := range u.rtm.IncomingEvents {
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				u.handleSlackActionPost(ev)
			case *slack.DisconnectedEvent:
				logger.Debug("disconnected event received, we should reconnect now..")
				//return
			}
		}
	}
	/*
			logger.Debugf("MMUser WsReceiver: %#v", message.Raw)
			// check if we have the users/channels in our cache. If not update
			u.checkWsActionMessage(message.Raw, updateChannelsThrottle)
			switch message.Raw.Event {
			case model.WEBSOCKET_EVENT_POSTED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_POST_EDITED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_USER_REMOVED:
				u.handleWsActionUserRemoved(message.Raw)
			case model.WEBSOCKET_EVENT_USER_ADDED:
				u.handleWsActionUserAdded(message.Raw)
			}
		}
	*/
}

func (u *User) handleSlackActionPost(rmsg *slack.MessageEvent) {
	var ch Channel
	logger.Debugf("handleSlackActionPost() receiving msg %#v", rmsg)
	if len(rmsg.Attachments) > 0 {
		// skip messages we made ourselves
		if rmsg.Attachments[0].CallbackID == "matterircd" {
			return
		}
	}

	user, err := u.rtm.GetUserInfo(rmsg.User)
	if err != nil {
		if rmsg.BotID == "" {
			return
		}
	}

	// handle bot messages
	botname := ""
	if rmsg.User == "" && rmsg.BotID != "" {
		botname = rmsg.Username
		if botname == "" {
			bot, _ := u.rtm.GetBotInfo(rmsg.BotID)
			if bot.Name != "" {
				botname = bot.Name
			}
		}
	}

	// create new "ghost" user
	ghost := u.createSlackUser(user)

	spoofUsername := ""
	if user != nil {
		spoofUsername = user.ID
		if ghost != nil {
			spoofUsername = ghost.Nick
			if ghost.DisplayName != "" && ghost.DisplayName != ghost.Nick {
				spoofUsername = "|"
				//	spoofUsername = ghost.DisplayName
			}
		}
	}

	// if we have a botname, use it
	if botname != "" {
		spoofUsername = botname
	}

	msgs := strings.Split(rmsg.Text, "\n")
	// direct message

	ch = u.Srv.Channel(rmsg.Channel)

	if ghost != nil {
		// join if not in channel
		if !ch.HasUser(ghost) {
			ch.Join(ghost)
		}
	}

	for _, m := range msgs {
		// cleanup the message
		m = u.replaceMention(m)
		m = u.replaceVariable(m)
		m = u.replaceChannel(m)
		m = u.replaceURL(m)
		m = html.UnescapeString(m)

		// look in attachments if we have no text
		if m == "" {
			for _, attach := range rmsg.Attachments {
				if attach.Text != "" {
					m = attach.Text
				} else {
					m = attach.Fallback
				}
			}
		}

		// still no text, ignore this message
		if m == "" {
			continue
		}

		if strings.HasPrefix(rmsg.Channel, "D") {
			u.MsgSpoofUser(spoofUsername, m)
		} else {
			if ghost != nil && ghost.DisplayName != "" && ghost.DisplayName != ghost.Nick {
				m = "<" + ghost.DisplayName + "> " + m
			}
			ch.SpoofMessage(spoofUsername, m)
		}
	}
}

// sync IRC with mattermost channel state
func (u *User) syncSlackChannel(id string, name string) {
	srv := u.Srv
	info, err := u.sc.GetChannelInfo(id)
	if err != nil {
		logger.Info(err)
	}

	for _, user := range info.Members {
		if u.sinfo.User.ID != user {
			//slackuser, _ := u.sc.GetUserInfo(user)
			slackuser := u.getSlackUser(user)
			if slackuser != nil {
				u.addSlackUserToChannel(slackuser, "#"+name, id)
			}
		}
	}

	ch := srv.Channel(id)
	ch.Topic(u, info.Topic.Value)
	if !ch.HasUser(u) {
		logger.Debugf("syncSlackchannel adding myself to %s (id: %s)", name, id)
		ch.Join(u)
	}
}

// sync IRC with mattermost channel state
func (u *User) syncSlackGroup(id string, name string) {
	srv := u.Srv
	info, err := u.sc.GetGroupInfo(id)
	if err != nil {
		logger.Info(err)
	}

	for _, user := range info.Members {
		if u.sinfo.User.ID != user {
			//slackuser, _ := u.sc.GetUserInfo(user)
			slackuser := u.getSlackUser(user)
			if slackuser != nil {
				u.addSlackUserToChannel(slackuser, "#"+name, id)
			}
		}
	}

	ch := srv.Channel(id)
	ch.Topic(u, info.Topic.Value)
	if !ch.HasUser(u) {
		logger.Debugf("syncSlackchannel adding myself to %s (id: %s)", name, id)
		ch.Join(u)
	}
}

// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
func (u *User) replaceMention(text string) string {
	results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, "<@"+r[1]+">", "@"+u.userName(r[1]), -1)
	}
	return text
}

// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
func (u *User) replaceChannel(text string) string {
	results := regexp.MustCompile(`<#[a-zA-Z0-9]+\|(.+?)>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, r[0], "#"+r[1], -1)
	}
	return text
}

// @see https://api.slack.com/docs/message-formatting#variables
func (u *User) replaceVariable(text string) string {
	results := regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		if r[2] != "" {
			text = strings.Replace(text, r[0], "@"+r[2], -1)
		} else {
			text = strings.Replace(text, r[0], "@"+r[1], -1)
		}
	}
	return text
}

// @see https://api.slack.com/docs/message-formatting#linking_to_urls
func (u *User) replaceURL(text string) string {
	results := regexp.MustCompile(`<(.*?)(\|.*?)?>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, r[0], r[1], -1)
	}
	return text
}

func (u *User) getSlackUser(name string) *slack.User {
	u.RLock()
	defer u.RUnlock()
	if user, ok := u.susers[name]; ok {
		return &user
	}
	return nil
}

func (u *User) userName(id string) string {
	u.RLock()
	defer u.RUnlock()
	// TODO dynamically update when new users are joining slack
	for _, us := range u.susers {
		if us.ID == id {
			if us.Profile.DisplayName != "" {
				return us.Profile.DisplayName
			}
			return us.Name
		}
	}
	if id == u.sinfo.User.ID {
		return u.sinfo.User.Name
	}
	return ""
}

func (u *User) isConnected() bool {
	return u.connected
}
<commit_msg>Add UseDisplayName config setting (slack)<commit_after>package irckit

import (
	"errors"
	"html"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/nlopes/slack"
)

type SlackInfo struct {
	Token      string
	sc         *slack.Client
	rtm        *slack.RTM
	sinfo      *slack.Info
	susers     map[string]slack.User
	connected  bool
	inprogress bool
	sync.RWMutex
}

func (u *User) loginToSlack() (*slack.Client, error) {
	u.sc = slack.New(u.Token)
	u.rtm = u.sc.NewRTM()
	u.Lock()
	u.susers = make(map[string]slack.User)
	u.Unlock()
	go u.rtm.ManageConnection()
	//time.Sleep(time.Second * 2)
	u.sinfo = u.rtm.GetInfo()
	count := 0
	for u.sinfo == nil {
		time.Sleep(time.Millisecond * 500)
		logger.Debug("still waiting for sinfo")
		u.sinfo = u.rtm.GetInfo()
		count++
		if count == 20 {
			return nil, errors.New("couldn't connect in 10 seconds. Check your credentials")
		}
	}

	// we only know which server we are connecting to when we actually are connected.
	// disconnect if we're not allowed
	if len(u.MmInfo.Cfg.SlackSettings.Restrict) > 0 {
		ok := false
		for _, domain := range u.MmInfo.Cfg.SlackSettings.Restrict {
			if domain == u.sinfo.Team.Domain {
				ok = true
				break
			}
		}
		if !ok {
			u.rtm.Disconnect()
			return nil, errors.New("Not allowed to connect to " + u.sinfo.Team.Domain + " slack")
		}
	}
	go u.handleSlack()
	u.addSlackUsersToChannels()
	u.connected = true
	return u.sc, nil
}

func (u *User) logoutFromSlack() error {
	logger.Debug("calling logout from slack")
	err := u.rtm.Disconnect()
	if err != nil {
		logger.Debug("logoutfrom slack", err)
		return err
	}
	u.Srv.Logout(u)
	u.sc = nil
	logger.Info("logout succeeded")
	u.connected = false
	return nil
}

func (u *User) createSlackUser(slackuser *slack.User) *User {
	if slackuser == nil {
		return nil
	}
	if ghost, ok := u.Srv.HasUser(slackuser.Name); ok {
		return ghost
	}
	ghost := &User{Nick: slackuser.Name, User: slackuser.ID, Real: slackuser.RealName, Host: "host", Roles: "", channels: map[Channel]struct{}{}, DisplayName: slackuser.Profile.DisplayName}
	ghost.MmGhostUser = true
	u.Srv.Add(ghost)
	return ghost
}

func (u *User) addSlackUserToChannel(user *slack.User, channel string, channelId string) {
	if user == nil {
		return
	}
	ghost := u.createSlackUser(user)
	if ghost == nil {
		logger.Warnf("Cannot join %v into %s", user, channel)
		return
	}
	logger.Debugf("adding %s to %s (%s)", ghost.Nick, channel, channelId)
	ch := u.Srv.Channel(channelId)
	logger.Debugf("channel: %#v %#v", ch.String(), ch.ID())
	ch.Join(ghost)
}

func (u *User) addSlackUsersToChannels() {
	srv := u.Srv
	throttle := time.Tick(time.Millisecond * 100)
	logger.Debug("in addUsersToChannels()")
	// add all users, also who are not on channels
	ch := srv.Channel("&users")
	users, _ := u.sc.GetUsers()
	for _, mmuser := range users {
		// do not add our own nick
		if mmuser.ID == u.sinfo.User.ID {
			continue
		}
		u.createSlackUser(&mmuser)
		u.addSlackUserToChannel(&mmuser, "&users", "&users")
		u.Lock()
		u.susers[mmuser.ID] = mmuser
		u.Unlock()
	}
	ch.Join(u)

	channels := make(chan interface{}, 10)
	for i := 0; i < 10; i++ {
		go u.addSlackUserToChannelWorker(channels, throttle)
	}
	groups, _ := u.sc.GetGroups(true)
	mmchannels, _ := u.sc.GetChannels(true)
	for _, mmchannel := range mmchannels {
		if mmchannel.IsMember {
			logger.Debug("Adding channel", mmchannel)
			channels <- mmchannel
		}
	}
	for _, mmchannel := range groups {
		logger.Debug("Adding private channel", mmchannel)
		channels <- mmchannel
	}
	close(channels)
}

func (u *User) addSlackUserToChannelWorker(channels <-chan interface{}, throttle <-chan time.Time) {
	var ID, name string
	for {
		mmchannel, ok := <-channels
		if !ok {
			logger.Debug("Done adding user to channels")
			return
		}
		<-throttle
		switch mmchannel.(type) {
		case slack.Channel:
			ID = mmchannel.(slack.Channel).ID
			name = mmchannel.(slack.Channel).Name
			u.syncSlackChannel(ID, name)
		case slack.Group:
			ID = mmchannel.(slack.Group).ID
			name = mmchannel.(slack.Group).Name
			logger.Debugf("GROUP %#v", mmchannel.(slack.Group))
			u.syncSlackGroup(ID, name)

		}
		// exclude direct messages
		//var spoof func(string, string)
		//ch := u.Srv.Channel(mmchannel.ID)
		// post everything to the channel you haven't seen yet
	}
}

func (u *User) handleSlack() {
	for {
		/*
			if u.mc.WsQuit {
				logger.Debug("exiting handleWsMessage")
				return
			}
		*/
		logger.Debug("in handleSlack")
		for msg := range u.rtm.IncomingEvents {
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				u.handleSlackActionPost(ev)
			case *slack.DisconnectedEvent:
				logger.Debug("disconnected event received, we should reconnect now..")
				//return
			}
		}
	}
	/*
			logger.Debugf("MMUser WsReceiver: %#v", message.Raw)
			// check if we have the users/channels in our cache. If not update
			u.checkWsActionMessage(message.Raw, updateChannelsThrottle)
			switch message.Raw.Event {
			case model.WEBSOCKET_EVENT_POSTED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_POST_EDITED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_USER_REMOVED:
				u.handleWsActionUserRemoved(message.Raw)
			case model.WEBSOCKET_EVENT_USER_ADDED:
				u.handleWsActionUserAdded(message.Raw)
			}
		}
	*/
}

func (u *User) handleSlackActionPost(rmsg *slack.MessageEvent) {
	var ch Channel
	logger.Debugf("handleSlackActionPost() receiving msg %#v", rmsg)
	if len(rmsg.Attachments) > 0 {
		// skip messages we made ourselves
		if rmsg.Attachments[0].CallbackID == "matterircd" {
			return
		}
	}

	user, err := u.rtm.GetUserInfo(rmsg.User)
	if err != nil {
		if rmsg.BotID == "" {
			return
		}
	}

	// handle bot messages
	botname := ""
	if rmsg.User == "" && rmsg.BotID != "" {
		botname = rmsg.Username
		if botname == "" {
			bot, _ := u.rtm.GetBotInfo(rmsg.BotID)
			if bot.Name != "" {
				botname = bot.Name
			}
		}
	}

	// create new "ghost" user
	ghost := u.createSlackUser(user)

	spoofUsername := ""
	if user != nil {
		spoofUsername = user.ID
		if ghost != nil {
			spoofUsername = ghost.Nick
			if ghost.DisplayName != "" && ghost.DisplayName != ghost.Nick && u.MmInfo.Cfg.SlackSettings.UseDisplayName {
				spoofUsername = "|"
				//	spoofUsername = ghost.DisplayName
			}
		}
	}

	// if we have a botname, use it
	if botname != "" {
		spoofUsername = botname
	}

	msgs := strings.Split(rmsg.Text, "\n")
	// direct message

	ch = u.Srv.Channel(rmsg.Channel)

	if ghost != nil {
		// join if not in channel
		if !ch.HasUser(ghost) {
			ch.Join(ghost)
		}
	}

	for _, m := range msgs {
		// cleanup the message
		m = u.replaceMention(m)
		m = u.replaceVariable(m)
		m = u.replaceChannel(m)
		m = u.replaceURL(m)
		m = html.UnescapeString(m)

		// look in attachments if we have no text
		if m == "" {
			for _, attach := range rmsg.Attachments {
				if attach.Text != "" {
					m = attach.Text
				} else {
					m = attach.Fallback
				}
			}
		}

		// still no text, ignore this message
		if m == "" {
			continue
		}

		if strings.HasPrefix(rmsg.Channel, "D") {
			u.MsgSpoofUser(spoofUsername, m)
		} else {
			if ghost != nil && ghost.DisplayName != "" && ghost.DisplayName != ghost.Nick &&
				u.MmInfo.Cfg.SlackSettings.UseDisplayName {
				m = "<" + ghost.DisplayName + "> " + m
			}
			ch.SpoofMessage(spoofUsername, m)
		}
	}
}

// sync IRC with mattermost channel state
func (u *User) syncSlackChannel(id string, name string) {
	srv := u.Srv
	info, err := u.sc.GetChannelInfo(id)
	if err != nil {
		logger.Info(err)
	}

	for _, user := range info.Members {
		if u.sinfo.User.ID != user {
			//slackuser, _ := u.sc.GetUserInfo(user)
			slackuser := u.getSlackUser(user)
			if slackuser != nil {
				u.addSlackUserToChannel(slackuser, "#"+name, id)
			}
		}
	}

	ch := srv.Channel(id)
	ch.Topic(u, info.Topic.Value)
	if !ch.HasUser(u) {
		logger.Debugf("syncSlackchannel adding myself to %s (id: %s)", name, id)
		ch.Join(u)
	}
}

// sync IRC with mattermost channel state
func (u *User) syncSlackGroup(id string, name string) {
	srv := u.Srv
	info, err := u.sc.GetGroupInfo(id)
	if err != nil {
		logger.Info(err)
	}

	for _, user := range info.Members {
		if u.sinfo.User.ID != user {
			//slackuser, _ := u.sc.GetUserInfo(user)
			slackuser := u.getSlackUser(user)
			if slackuser != nil {
				u.addSlackUserToChannel(slackuser, "#"+name, id)
			}
		}
	}

	ch := srv.Channel(id)
	ch.Topic(u, info.Topic.Value)
	if !ch.HasUser(u) {
		logger.Debugf("syncSlackchannel adding myself to %s (id: %s)", name, id)
		ch.Join(u)
	}
}

// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
func (u *User) replaceMention(text string) string {
	results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, "<@"+r[1]+">", "@"+u.userName(r[1]), -1)
	}
	return text
}

// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
func (u *User) replaceChannel(text string) string {
	results := regexp.MustCompile(`<#[a-zA-Z0-9]+\|(.+?)>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, r[0], "#"+r[1], -1)
	}
	return text
}

// @see https://api.slack.com/docs/message-formatting#variables
func (u *User) replaceVariable(text string) string {
	results := regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		if r[2] != "" {
			text = strings.Replace(text, r[0], "@"+r[2], -1)
		} else {
			text = strings.Replace(text, r[0], "@"+r[1], -1)
		}
	}
	return text
}

// @see https://api.slack.com/docs/message-formatting#linking_to_urls
func (u *User) replaceURL(text string) string {
	results := regexp.MustCompile(`<(.*?)(\|.*?)?>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, r[0], r[1], -1)
	}
	return text
}

func (u *User) getSlackUser(name string) *slack.User {
	u.RLock()
	defer u.RUnlock()
	if user, ok := u.susers[name]; ok {
		return &user
	}
	return nil
}

func (u *User) userName(id string) string {
	u.RLock()
	defer u.RUnlock()
	// TODO dynamically update when new users are joining slack
	for _, us := range u.susers {
		if us.ID == id {
			if us.Profile.DisplayName != "" {
				return us.Profile.DisplayName
			}
			return us.Name
		}
	}
	if id == u.sinfo.User.ID {
		return u.sinfo.User.Name
	}
	return ""
}

func (u *User) isConnected() bool {
	return u.connected
}
<|endoftext|>