<commit_before>/*
Copyright 2021 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 app

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"testing"
	"time"

	"k8s.io/dns/pkg/dns/config"
)

const (
	templateCoreFileContents = `
cluster.local:53 {
    errors
    cache {
            success 9984 30
            denial 9984 5
    }
    reload
    loop
    bind __PILLAR__LOCAL__DNS__
    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
    }
    prometheus :9253
    }
.:53 {
    errors
    cache 30
    reload
    loop
    bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
    forward . __PILLAR__UPSTREAM__SERVERS__ {
            force_tcp
    }
    prometheus :9253
    }
`
	templateCoreFileName   = "testCoreFile.base"
	coreFileName           = "testCoreFile"
	cmDirName              = "testKubeDNSDir"
	stubDomainFileName     = "stubDomains"
	upstreamServerFileName = "upstreamNameservers"
	UpstreamClusterDNS     = "test-svc"
)

func updateStubDomainsAndUpstreamServers(t *testing.T, p *ConfigParams, c *config.Config) string {
	if stubDomainBlob, err := json.Marshal(c.StubDomains); err != nil {
		t.Errorf("Failed to marshal stubdomains info, err %v", err)
	} else {
		if err := ioutil.WriteFile(filepath.Join(p.KubednsCMPath, stubDomainFileName), stubDomainBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", stubDomainFileName, err)
		}
	}

	if upstreamBlob, err := json.Marshal(c.UpstreamNameservers); err != nil {
		t.Errorf("Failed to marshal upstream nameservers info, err %v", err)
	} else {
		if err = ioutil.WriteFile(filepath.Join(p.KubednsCMPath, upstreamServerFileName), upstreamBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", upstreamServerFileName, err)
		}
	}
	return ""
}

func updateBaseFile(t *testing.T, p *ConfigParams, newContents []byte) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(newContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to update template config file - %v", err)
	}
}

func createBaseFiles(t *testing.T, p *ConfigParams) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(templateCoreFileContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to write template config file - %v", err)
	}
	if err := os.Mkdir(p.KubednsCMPath, os.ModePerm); err != nil {
		t.Fatalf("Failed to create KubeDNS configmap dir - %v", err)
	}
}

func compareFileContents(filename, contents string, t *testing.T) (string, int) {
	out, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Errorf("Failed to read file %s , err %v", filename, err)
		return "", -1
	}
	return string(out), strings.Compare(string(out), contents)
}

func stubDomainsEqual(str1, str2 string, t *testing.T) bool {
	// Double newline separates one stubdomain block from next
	blocks1 := strings.Split(str1, "\n\n")
	blocks2 := strings.Split(str2, "\n\n")
	if len(blocks1) != len(blocks2) {
		return false
	}
	sort.Strings(blocks1)
	sort.Strings(blocks2)
	for i, v := range blocks1 {
		if v != blocks2[i] {
			// Printing raw bytes is more useful to identify the inequality
			t.Errorf("Stubdomains not equal - %+v and %+v", []byte(v), []byte(blocks2[i]))
			return false
		}
	}
	return true
}

func TestUpdateCoreFile(t *testing.T) {
	baseDir, err := ioutil.TempDir("", "dnstest")
	if err != nil {
		t.Fatalf("Failed to obtain temp directory for testing, err %v", err)
	}
	envName := strings.ToUpper(strings.Replace(UpstreamClusterDNS, "-", "_", -1)) + "_SERVICE_HOST"
	os.Setenv(envName, "9.10.11.12")
	defer func() { os.RemoveAll(baseDir) }()
	c, err := NewCacheApp(&ConfigParams{LocalIPStr: "169.254.20.10,10.0.0.10",
		LocalPort:       "53",
		BaseCoreFile:    filepath.Join(baseDir, templateCoreFileName),
		CoreFile:        filepath.Join(baseDir, coreFileName),
		KubednsCMPath:   filepath.Join(baseDir, cmDirName),
		UpstreamSvcName: UpstreamClusterDNS,
		SetupIptables:   false,
	})
	if err != nil {
		t.Fatalf("Failed to obtain CacheApp instance, err %v", err)
	}
	createBaseFiles(t, c.params)
	c.initDNSConfigSync()
	// listenIP to bind plugin should be space-separated.
	listenIPs := strings.Replace(c.params.LocalIPStr, ",", " ", -1)
	r := strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "9.10.11.12",
		UpstreamServerVar, "/etc/resolv.conf",
		LocalDNSServerVar, "")
	expectedContents := r.Replace(templateCoreFileContents)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("Expected contents '%s', Got '%s'", expectedContents, out)
	}
	if strings.Contains(expectedContents, "PILLAR") {
		t.Errorf("Not all variables were substituted in file, Got '%s'", expectedContents)
	}

	// Modify the template file to mimic node-local-dns configmap being updated.
	// Replace "loop" plugin with "template" as an example config change.
	newTemplateContents := strings.Replace(templateCoreFileContents, "loop", "template", -1)
	updateBaseFile(t, c.params, []byte(newTemplateContents))
	expectedContents = r.Replace(newTemplateContents)
	time.Sleep(15 * time.Second)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("After basefile change, expected contents '%s', Got '%s'", expectedContents, out)
	}
	customConfig := &config.Config{StubDomains: map[string][]string{
		"acme.local":   {"1.1.1.1"},
		"google.local": {"google-public-dns-a.google.com"},
		"widget.local": {"2.2.2.2:10053", "3.3.3.3"},
	},
		UpstreamNameservers: []string{"2.2.2.2:10053", "3.3.3.3"},
	}
	updateStubDomainsAndUpstreamServers(t, c.params, customConfig)
	upstreamUDP := strings.Replace(upstreamUDPBlock, UpstreamServerVar,
		strings.Join(customConfig.UpstreamNameservers, " "), -1)
	r = strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "9.10.11.12",
		LocalDNSServerVar, "",
		upstreamTCPBlock, upstreamUDP)
	expectedContents = r.Replace(newTemplateContents)
	expectedStubStr := getStubDomainStr(customConfig.StubDomains, &stubDomainInfo{Port: c.params.LocalPort, CacheTTL: defaultTTL,
		LocalIP: strings.Replace(c.params.LocalIPStr, ",", " ", -1)})

	time.Sleep(15 * time.Second)
	out, _ := compareFileContents(c.params.CoreFile, expectedContents, t)
	if !strings.Contains(out, expectedContents) {
		t.Fatalf("Could not find contents '%s' in CoreFile, Got '%s'", expectedContents, out)
	}
	// The entire file cannot be compared because the stubDomains block
	// will be in a different order as they are generated by iterating over
	// a map. They will be converted  to a list and compared individually.
	stubStr := strings.TrimPrefix(out, expectedContents)
	if !stubDomainsEqual(strings.TrimSpace(stubStr), strings.TrimSpace(expectedStubStr), t) {
		t.Fail()
	}
}

func TestUpdateIPv6CoreFile(t *testing.T) {
	baseDir, err := ioutil.TempDir("", "dnstest")
	if err != nil {
		t.Fatalf("Failed to obtain temp directory for testing, err %v", err)
	}
	envName := strings.ToUpper(strings.Replace(UpstreamClusterDNS, "-", "_", -1)) + "_SERVICE_HOST"
	os.Setenv(envName, "2001:db8::1")
	defer func() { os.RemoveAll(baseDir) }()
	c, err := NewCacheApp(&ConfigParams{LocalIPStr: "fe80:169:254::1,fd00:1:2:3::5",
		LocalPort:       "53",
		BaseCoreFile:    filepath.Join(baseDir, templateCoreFileName),
		CoreFile:        filepath.Join(baseDir, coreFileName),
		KubednsCMPath:   filepath.Join(baseDir, cmDirName),
		UpstreamSvcName: UpstreamClusterDNS,
		SetupIptables:   false,
	})
	if err != nil {
		t.Fatalf("Failed to obtain CacheApp instance, err %v", err)
	}
	createBaseFiles(t, c.params)
	c.initDNSConfigSync()
	// listenIP to bind plugin should be space-separated.
	listenIPs := strings.Replace(c.params.LocalIPStr, ",", " ", -1)
	r := strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "2001:db8::1",
		UpstreamServerVar, "/etc/resolv.conf",
		LocalDNSServerVar, "")
	expectedContents := r.Replace(templateCoreFileContents)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("Expected contents '%s', Got '%s'", expectedContents, out)
	}
	if strings.Contains(expectedContents, "PILLAR") {
		t.Errorf("Not all variables were substituted in file, Got '%s'", expectedContents)
	}

	// Modify the template file to mimic node-local-dns configmap being updated.
	// Replace "loop" plugin with "template" as an example config change.
	newTemplateContents := strings.Replace(templateCoreFileContents, "loop", "template", -1)
	updateBaseFile(t, c.params, []byte(newTemplateContents))
	expectedContents = r.Replace(newTemplateContents)
	time.Sleep(15 * time.Second)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("After basefile change, expected contents '%s', Got '%s'", expectedContents, out)
	}
	customConfig := &config.Config{StubDomains: map[string][]string{
		"acme.local":   {"2001:db8:1:1:1::1"},
		"google.local": {"google-public-dns-a.google.com"},
		"widget.local": {"[2001:db8:2:2:2::2]:10053", "2001:db8:3:3:3::3"},
	},
		UpstreamNameservers: []string{"[2001:db8:2:2:2::2]:10053", "2001:db8:3:3:3::3"},
	}
	updateStubDomainsAndUpstreamServers(t, c.params, customConfig)
	upstreamUDP := strings.Replace(upstreamUDPBlock, UpstreamServerVar,
		strings.Join(customConfig.UpstreamNameservers, " "), -1)
	r = strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "2001:db8::1",
		LocalDNSServerVar, "",
		upstreamTCPBlock, upstreamUDP)
	expectedContents = r.Replace(newTemplateContents)
	expectedStubStr := getStubDomainStr(customConfig.StubDomains, &stubDomainInfo{Port: c.params.LocalPort, CacheTTL: defaultTTL,
		LocalIP: strings.Replace(c.params.LocalIPStr, ",", " ", -1)})

	time.Sleep(15 * time.Second)
	out, _ := compareFileContents(c.params.CoreFile, expectedContents, t)
	if !strings.Contains(out, expectedContents) {
		t.Fatalf("Could not find contents '%s' in CoreFile, Got '%s'", expectedContents, out)
	}
	// The entire file cannot be compared because the stubDomains block
	// will be in a different order as they are generated by iterating over
	// a map. They will be converted  to a list and compared individually.
	stubStr := strings.TrimPrefix(out, expectedContents)
	if !stubDomainsEqual(strings.TrimSpace(stubStr), strings.TrimSpace(expectedStubStr), t) {
		t.Fail()
	}
}
<commit_msg>Fix the integration test<commit_after>/*
Copyright 2021 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 app

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"testing"
	"time"

	"k8s.io/dns/pkg/dns/config"
)

const (
	templateCoreFileContents = `
cluster.local:53 {
    errors
    cache {
            success 9984 30
            denial 9984 5
    }
    reload
    loop
    bind __PILLAR__LOCAL__DNS__
    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
    }
    prometheus :9253
    }
.:53 {
    errors
    cache 30
    reload
    loop
    bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
    forward . __PILLAR__UPSTREAM__SERVERS__ {
            force_tcp
    }
    prometheus :9253
    }
`
	templateCoreFileName   = "testCoreFile.base"
	coreFileName           = "testCoreFile"
	cmDirName              = "testKubeDNSDir"
	stubDomainFileName     = "stubDomains"
	upstreamServerFileName = "upstreamNameservers"
	UpstreamClusterDNS     = "test-svc"
)

func updateStubDomainsAndUpstreamServers(t *testing.T, p *ConfigParams, c *config.Config) string {
	if stubDomainBlob, err := json.Marshal(c.StubDomains); err != nil {
		t.Errorf("Failed to marshal stubdomains info, err %v", err)
	} else {
		if err := ioutil.WriteFile(filepath.Join(p.KubednsCMPath, stubDomainFileName), stubDomainBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", stubDomainFileName, err)
		}
	}

	if upstreamBlob, err := json.Marshal(c.UpstreamNameservers); err != nil {
		t.Errorf("Failed to marshal upstream nameservers info, err %v", err)
	} else {
		if err = ioutil.WriteFile(filepath.Join(p.KubednsCMPath, upstreamServerFileName), upstreamBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", upstreamServerFileName, err)
		}
	}
	return ""
}

func updateBaseFile(t *testing.T, p *ConfigParams, newContents []byte) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(newContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to update template config file - %v", err)
	}
}

func createBaseFiles(t *testing.T, p *ConfigParams) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(templateCoreFileContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to write template config file - %v", err)
	}
	if err := os.Mkdir(p.KubednsCMPath, os.ModePerm); err != nil {
		t.Fatalf("Failed to create KubeDNS configmap dir - %v", err)
	}
}

func compareFileContents(filename, contents string, t *testing.T) (string, int) {
	out, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Errorf("Failed to read file %s , err %v", filename, err)
		return "", -1
	}
	return string(out), strings.Compare(string(out), contents)
}

func stubDomainsEqual(str1, str2 string, t *testing.T) bool {
	// Double newline separates one stubdomain block from next
	blocks1 := strings.Split(str1, "\n\n")
	blocks2 := strings.Split(str2, "\n\n")
	if len(blocks1) != len(blocks2) {
		return false
	}
	sort.Strings(blocks1)
	sort.Strings(blocks2)
	for i, v := range blocks1 {
		if v != blocks2[i] {
			// Printing raw bytes is more useful to identify the inequality
			t.Errorf("Stubdomains not equal - %+v and %+v", []byte(v), []byte(blocks2[i]))
			return false
		}
	}
	return true
}

func TestUpdateCoreFile(t *testing.T) {
	baseDir, err := ioutil.TempDir("", "dnstest")
	if err != nil {
		t.Fatalf("Failed to obtain temp directory for testing, err %v", err)
	}
	envName := strings.ToUpper(strings.Replace(UpstreamClusterDNS, "-", "_", -1)) + "_SERVICE_HOST"
	os.Setenv(envName, "9.10.11.12")
	defer func() { os.RemoveAll(baseDir) }()
	c, err := NewCacheApp(&ConfigParams{LocalIPStr: "169.254.20.10,10.0.0.10",
		LocalPort:       "53",
		BaseCoreFile:    filepath.Join(baseDir, templateCoreFileName),
		CoreFile:        filepath.Join(baseDir, coreFileName),
		KubednsCMPath:   filepath.Join(baseDir, cmDirName),
		UpstreamSvcName: UpstreamClusterDNS,
		SetupIptables:   false,
	})
	if err != nil {
		t.Fatalf("Failed to obtain CacheApp instance, err %v", err)
	}
	createBaseFiles(t, c.params)
	c.initDNSConfigSync()
	// listenIP to bind plugin should be space-separated.
	listenIPs := strings.Replace(c.params.LocalIPStr, ",", " ", -1)
	r := strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "9.10.11.12",
		UpstreamServerVar, "/etc/resolv.conf",
		LocalDNSServerVar, "")
	expectedContents := r.Replace(templateCoreFileContents)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("Expected contents '%s', Got '%s'", expectedContents, out)
	}
	if strings.Contains(expectedContents, "PILLAR") {
		t.Errorf("Not all variables were substituted in file, Got '%s'", expectedContents)
	}

	// Modify the template file to mimic node-local-dns configmap being updated.
	// Replace "loop" plugin with "template" as an example config change.
	newTemplateContents := strings.Replace(templateCoreFileContents, "loop", "template", -1)
	updateBaseFile(t, c.params, []byte(newTemplateContents))
	expectedContents = r.Replace(newTemplateContents)
	time.Sleep(15 * time.Second)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("After basefile change, expected contents '%s', Got '%s'", expectedContents, out)
	}
	customConfig := &config.Config{StubDomains: map[string][]string{
		"acme.local":   {"1.1.1.1"},
		"google.local": {"8.8.8.8"},
		"widget.local": {"2.2.2.2:10053", "3.3.3.3"},
	},
		UpstreamNameservers: []string{"2.2.2.2:10053", "3.3.3.3"},
	}
	updateStubDomainsAndUpstreamServers(t, c.params, customConfig)
	upstreamUDP := strings.Replace(upstreamUDPBlock, UpstreamServerVar,
		strings.Join(customConfig.UpstreamNameservers, " "), -1)
	r = strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "9.10.11.12",
		LocalDNSServerVar, "",
		upstreamTCPBlock, upstreamUDP)
	expectedContents = r.Replace(newTemplateContents)
	expectedStubStr := getStubDomainStr(customConfig.StubDomains, &stubDomainInfo{Port: c.params.LocalPort, CacheTTL: defaultTTL,
		LocalIP: strings.Replace(c.params.LocalIPStr, ",", " ", -1)})

	time.Sleep(15 * time.Second)
	out, _ := compareFileContents(c.params.CoreFile, expectedContents, t)
	if !strings.Contains(out, expectedContents) {
		t.Fatalf("Could not find contents '%s' in CoreFile, Got '%s'", expectedContents, out)
	}
	// The entire file cannot be compared because the stubDomains block
	// will be in a different order as they are generated by iterating over
	// a map. They will be converted  to a list and compared individually.
	stubStr := strings.TrimPrefix(out, expectedContents)
	if !stubDomainsEqual(strings.TrimSpace(stubStr), strings.TrimSpace(expectedStubStr), t) {
		t.Fail()
	}
}

func TestUpdateIPv6CoreFile(t *testing.T) {
	baseDir, err := ioutil.TempDir("", "dnstest")
	if err != nil {
		t.Fatalf("Failed to obtain temp directory for testing, err %v", err)
	}
	envName := strings.ToUpper(strings.Replace(UpstreamClusterDNS, "-", "_", -1)) + "_SERVICE_HOST"
	os.Setenv(envName, "2001:db8::1")
	defer func() { os.RemoveAll(baseDir) }()
	c, err := NewCacheApp(&ConfigParams{LocalIPStr: "fe80:169:254::1,fd00:1:2:3::5",
		LocalPort:       "53",
		BaseCoreFile:    filepath.Join(baseDir, templateCoreFileName),
		CoreFile:        filepath.Join(baseDir, coreFileName),
		KubednsCMPath:   filepath.Join(baseDir, cmDirName),
		UpstreamSvcName: UpstreamClusterDNS,
		SetupIptables:   false,
	})
	if err != nil {
		t.Fatalf("Failed to obtain CacheApp instance, err %v", err)
	}
	createBaseFiles(t, c.params)
	c.initDNSConfigSync()
	// listenIP to bind plugin should be space-separated.
	listenIPs := strings.Replace(c.params.LocalIPStr, ",", " ", -1)
	r := strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "2001:db8::1",
		UpstreamServerVar, "/etc/resolv.conf",
		LocalDNSServerVar, "")
	expectedContents := r.Replace(templateCoreFileContents)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("Expected contents '%s', Got '%s'", expectedContents, out)
	}
	if strings.Contains(expectedContents, "PILLAR") {
		t.Errorf("Not all variables were substituted in file, Got '%s'", expectedContents)
	}

	// Modify the template file to mimic node-local-dns configmap being updated.
	// Replace "loop" plugin with "template" as an example config change.
	newTemplateContents := strings.Replace(templateCoreFileContents, "loop", "template", -1)
	updateBaseFile(t, c.params, []byte(newTemplateContents))
	expectedContents = r.Replace(newTemplateContents)
	time.Sleep(15 * time.Second)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("After basefile change, expected contents '%s', Got '%s'", expectedContents, out)
	}
	customConfig := &config.Config{StubDomains: map[string][]string{
		"acme.local":   {"2001:db8:1:1:1::1"},
		"google.local": {"8.8.8.8"},
		"widget.local": {"[2001:db8:2:2:2::2]:10053", "2001:db8:3:3:3::3"},
	},
		UpstreamNameservers: []string{"[2001:db8:2:2:2::2]:10053", "2001:db8:3:3:3::3"},
	}
	updateStubDomainsAndUpstreamServers(t, c.params, customConfig)
	upstreamUDP := strings.Replace(upstreamUDPBlock, UpstreamServerVar,
		strings.Join(customConfig.UpstreamNameservers, " "), -1)
	r = strings.NewReplacer(LocalListenIPsVar, listenIPs,
		UpstreamClusterDNSVar, "2001:db8::1",
		LocalDNSServerVar, "",
		upstreamTCPBlock, upstreamUDP)
	expectedContents = r.Replace(newTemplateContents)
	expectedStubStr := getStubDomainStr(customConfig.StubDomains, &stubDomainInfo{Port: c.params.LocalPort, CacheTTL: defaultTTL,
		LocalIP: strings.Replace(c.params.LocalIPStr, ",", " ", -1)})

	time.Sleep(15 * time.Second)
	out, _ := compareFileContents(c.params.CoreFile, expectedContents, t)
	if !strings.Contains(out, expectedContents) {
		t.Fatalf("Could not find contents '%s' in CoreFile, Got '%s'", expectedContents, out)
	}
	// The entire file cannot be compared because the stubDomains block
	// will be in a different order as they are generated by iterating over
	// a map. They will be converted  to a list and compared individually.
	stubStr := strings.TrimPrefix(out, expectedContents)
	if !stubDomainsEqual(strings.TrimSpace(stubStr), strings.TrimSpace(expectedStubStr), t) {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package app

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"testing"
	"time"

	"k8s.io/dns/pkg/dns/config"
)

const (
	templateCoreFileContents = `
	cluster.local:53 {
        errors
        cache {
                success 9984 30
                denial 9984 5
        }
        reload
        loop
        bind __PILLAR__LOCAL__DNS__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        }
    .:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__
        forward . __PILLAR__UPSTREAM__SERVERS__ {
                force_tcp
        }
        prometheus :9253
        }
	`
	templateCoreFileName   = "testCoreFile.base"
	coreFileName           = "testCoreFile"
	cmDirName              = "testKubeDNSDir"
	stubDomainFileName     = "stubDomains"
	upstreamServerFileName = "upstreamNameservers"
	UpstreamClusterDNS     = "test-svc"
)

func updateStubDomainsAndUpstreamServers(t *testing.T, p *ConfigParams, c *config.Config) string {
	if stubDomainBlob, err := json.Marshal(c.StubDomains); err != nil {
		t.Errorf("Failed to marshal stubdomains info, err %v", err)
	} else {
		if err := ioutil.WriteFile(filepath.Join(p.KubednsCMPath, stubDomainFileName), stubDomainBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", stubDomainFileName, err)
		}
	}

	if upstreamBlob, err := json.Marshal(c.UpstreamNameservers); err != nil {
		t.Errorf("Failed to marshal upstream nameservers info, err %v", err)
	} else {
		if err = ioutil.WriteFile(filepath.Join(p.KubednsCMPath, upstreamServerFileName), upstreamBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", upstreamServerFileName, err)
		}
	}
	return ""
}

func createBaseFiles(t *testing.T, p *ConfigParams) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(templateCoreFileContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to write template config file - %v", err)
	}
	if err := os.Mkdir(p.KubednsCMPath, os.ModePerm); err != nil {
		t.Fatalf("Failed to create KubeDNS configmap dir - %v", err)
	}
}

func compareFileContents(filename, contents string, t *testing.T) (string, int) {
	out, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Errorf("Failed to read file %s , err %v", filename, err)
		return "", -1
	}
	return string(out), strings.Compare(string(out), contents)
}

func stubDomainsEqual(str1, str2 string, t *testing.T) bool {
	// Double newline separates one stubdomain block from next
	blocks1 := strings.Split(str1, "\n\n")
	blocks2 := strings.Split(str2, "\n\n")
	if len(blocks1) != len(blocks2) {
		return false
	}
	sort.Strings(blocks1)
	sort.Strings(blocks2)
	for i, v := range blocks1 {
		if v != blocks2[i] {
			// Printing raw bytes is more useful to identify the inequality
			t.Errorf("Stubdomains not equal - %+v and %+v", []byte(v), []byte(blocks2[i]))
			return false
		}
	}
	return true
}

func TestUpdateCoreFile(t *testing.T) {
	baseDir, err := ioutil.TempDir("", "dnstest")
	if err != nil {
		t.Fatalf("Failed to obtain temp directory for testing, err %v", err)
	}
	envName := strings.ToUpper(strings.Replace(UpstreamClusterDNS, "-", "_", -1)) + "_SERVICE_HOST"
	os.Setenv(envName, "9.10.11.12")
	defer func() { os.RemoveAll(baseDir) }()
	c, err := NewCacheApp(&ConfigParams{LocalIPStr: "169.254.20.10,10.0.0.10",
		LocalPort:       "53",
		BaseCoreFile:    filepath.Join(baseDir, templateCoreFileName),
		CoreFile:        filepath.Join(baseDir, coreFileName),
		KubednsCMPath:   filepath.Join(baseDir, cmDirName),
		UpstreamSvcName: UpstreamClusterDNS,
		SetupIptables:   false,
	})
	if err != nil {
		t.Fatalf("Failed to obtain CacheApp instance, err %v", err)
	}
	createBaseFiles(t, c.params)
	c.initKubeDNSConfigSync()
	r := strings.NewReplacer("__PILLAR__LOCAL__DNS__", c.params.LocalIPStr, "__PILLAR__CLUSTER__DNS__", "9.10.11.12",
		"__PILLAR__UPSTREAM__SERVERS__", "/etc/resolv.conf")
	expectedContents := r.Replace(templateCoreFileContents)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("Expected contents '%s', Got '%s'", expectedContents, out)
	}
	customConfig := &config.Config{StubDomains: map[string][]string{
		"acme.local":   {"1.1.1.1"},
		"google.local": {"google-public-dns-a.google.com"},
		"widget.local": {"2.2.2.2:10053", "3.3.3.3"},
	},
		UpstreamNameservers: []string{"2.2.2.2:10053", "3.3.3.3"},
	}
	updateStubDomainsAndUpstreamServers(t, c.params, customConfig)
	expectedContents = strings.Replace(expectedContents, "/etc/resolv.conf", strings.Join(customConfig.UpstreamNameservers, " "), -1)
	expectedStubStr := getStubDomainStr(customConfig.StubDomains, &stubDomainInfo{Port: c.params.LocalPort, CacheTTL: defaultTTL,
		LocalIP: strings.Replace(c.params.LocalIPStr, ",", " ", -1)})

	time.Sleep(15 * time.Second)
	out, _ := compareFileContents(c.params.CoreFile, expectedContents, t)
	if !strings.Contains(out, expectedContents) {
		t.Fatalf("Could not find contents '%s' in CoreFile '%s'", expectedContents, out)
	}
	// The entire file cannot be compared because the stubDomains block
	// will be in a different order as they are generated by iterating over
	// a map. They will be converted  to a list and compared individually.
	stubStr := strings.TrimPrefix(out, expectedContents)
	if !stubDomainsEqual(strings.TrimSpace(stubStr), strings.TrimSpace(expectedStubStr), t) {
		t.Fail()
	}
}
<commit_msg>Test to verify corefile edits, UDP for stubdomains<commit_after>package app

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"testing"
	"time"

	"k8s.io/dns/pkg/dns/config"
)

const (
	templateCoreFileContents = `
cluster.local:53 {
    errors
    cache {
            success 9984 30
            denial 9984 5
    }
    reload
    loop
    bind __PILLAR__LOCAL__DNS__
    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
    }
    prometheus :9253
    }
.:53 {
    errors
    cache 30
    reload
    loop
    bind __PILLAR__LOCAL__DNS__
    forward . __PILLAR__UPSTREAM__SERVERS__ {
            force_tcp
    }
    prometheus :9253
    }
`
	templateCoreFileName   = "testCoreFile.base"
	coreFileName           = "testCoreFile"
	cmDirName              = "testKubeDNSDir"
	stubDomainFileName     = "stubDomains"
	upstreamServerFileName = "upstreamNameservers"
	UpstreamClusterDNS     = "test-svc"
)

func updateStubDomainsAndUpstreamServers(t *testing.T, p *ConfigParams, c *config.Config) string {
	if stubDomainBlob, err := json.Marshal(c.StubDomains); err != nil {
		t.Errorf("Failed to marshal stubdomains info, err %v", err)
	} else {
		if err := ioutil.WriteFile(filepath.Join(p.KubednsCMPath, stubDomainFileName), stubDomainBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", stubDomainFileName, err)
		}
	}

	if upstreamBlob, err := json.Marshal(c.UpstreamNameservers); err != nil {
		t.Errorf("Failed to marshal upstream nameservers info, err %v", err)
	} else {
		if err = ioutil.WriteFile(filepath.Join(p.KubednsCMPath, upstreamServerFileName), upstreamBlob, os.ModePerm); err != nil {
			t.Errorf("Failed to write stubDomains file - %s, err %v", upstreamServerFileName, err)
		}
	}
	return ""
}

func updateBaseFile(t *testing.T, p *ConfigParams, newContents []byte) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(newContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to update template config file - %v", err)
	}
}

func createBaseFiles(t *testing.T, p *ConfigParams) {
	if err := ioutil.WriteFile(p.BaseCoreFile, []byte(templateCoreFileContents), os.ModePerm); err != nil {
		t.Fatalf("Failed to write template config file - %v", err)
	}
	if err := os.Mkdir(p.KubednsCMPath, os.ModePerm); err != nil {
		t.Fatalf("Failed to create KubeDNS configmap dir - %v", err)
	}
}

func compareFileContents(filename, contents string, t *testing.T) (string, int) {
	out, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Errorf("Failed to read file %s , err %v", filename, err)
		return "", -1
	}
	return string(out), strings.Compare(string(out), contents)
}

func stubDomainsEqual(str1, str2 string, t *testing.T) bool {
	// Double newline separates one stubdomain block from next
	blocks1 := strings.Split(str1, "\n\n")
	blocks2 := strings.Split(str2, "\n\n")
	if len(blocks1) != len(blocks2) {
		return false
	}
	sort.Strings(blocks1)
	sort.Strings(blocks2)
	for i, v := range blocks1 {
		if v != blocks2[i] {
			// Printing raw bytes is more useful to identify the inequality
			t.Errorf("Stubdomains not equal - %+v and %+v", []byte(v), []byte(blocks2[i]))
			return false
		}
	}
	return true
}

func TestUpdateCoreFile(t *testing.T) {
	baseDir, err := ioutil.TempDir("", "dnstest")
	if err != nil {
		t.Fatalf("Failed to obtain temp directory for testing, err %v", err)
	}
	envName := strings.ToUpper(strings.Replace(UpstreamClusterDNS, "-", "_", -1)) + "_SERVICE_HOST"
	os.Setenv(envName, "9.10.11.12")
	defer func() { os.RemoveAll(baseDir) }()
	c, err := NewCacheApp(&ConfigParams{LocalIPStr: "169.254.20.10,10.0.0.10",
		LocalPort:       "53",
		BaseCoreFile:    filepath.Join(baseDir, templateCoreFileName),
		CoreFile:        filepath.Join(baseDir, coreFileName),
		KubednsCMPath:   filepath.Join(baseDir, cmDirName),
		UpstreamSvcName: UpstreamClusterDNS,
		SetupIptables:   false,
	})
	if err != nil {
		t.Fatalf("Failed to obtain CacheApp instance, err %v", err)
	}
	createBaseFiles(t, c.params)
	c.initDNSConfigSync()
	// listenIP to bind plugin should be space-separated.
	listenIPs := strings.Replace(c.params.LocalIPStr, ",", " ", -1)
	r := strings.NewReplacer("__PILLAR__LOCAL__DNS__", listenIPs, "__PILLAR__CLUSTER__DNS__", "9.10.11.12",
		"__PILLAR__UPSTREAM__SERVERS__", "/etc/resolv.conf")
	expectedContents := r.Replace(templateCoreFileContents)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("Expected contents '%s', Got '%s'", expectedContents, out)
	}
	// Modify the template file to mimic node-local-dns configmap being updated.
	// Replace "loop" plugin with "template" as an example config change.
	newTemplateContents := strings.Replace(templateCoreFileContents, "loop", "template", -1)
	updateBaseFile(t, c.params, []byte(newTemplateContents))
	expectedContents = r.Replace(newTemplateContents)
	time.Sleep(15 * time.Second)
	if out, diff := compareFileContents(c.params.CoreFile, expectedContents, t); diff != 0 {
		t.Errorf("After basefile change, expected contents '%s', Got '%s'", expectedContents, out)
	}
	customConfig := &config.Config{StubDomains: map[string][]string{
		"acme.local":   {"1.1.1.1"},
		"google.local": {"google-public-dns-a.google.com"},
		"widget.local": {"2.2.2.2:10053", "3.3.3.3"},
	},
		UpstreamNameservers: []string{"2.2.2.2:10053", "3.3.3.3"},
	}
	updateStubDomainsAndUpstreamServers(t, c.params, customConfig)
	upstreamUDP := strings.Replace(upstreamUDPBlock, "__PILLAR__UPSTREAM__SERVERS__",
		strings.Join(customConfig.UpstreamNameservers, " "), -1)
	r = strings.NewReplacer("__PILLAR__LOCAL__DNS__", listenIPs, "__PILLAR__CLUSTER__DNS__", "9.10.11.12",
		upstreamBlock, upstreamUDP)
	expectedContents = r.Replace(newTemplateContents)
	expectedStubStr := getStubDomainStr(customConfig.StubDomains, &stubDomainInfo{Port: c.params.LocalPort, CacheTTL: defaultTTL,
		LocalIP: strings.Replace(c.params.LocalIPStr, ",", " ", -1)})

	time.Sleep(15 * time.Second)
	out, _ := compareFileContents(c.params.CoreFile, expectedContents, t)
	if !strings.Contains(out, expectedContents) {
		t.Fatalf("Could not find contents '%s' in CoreFile, Got '%s'", expectedContents, out)
	}
	// The entire file cannot be compared because the stubDomains block
	// will be in a different order as they are generated by iterating over
	// a map. They will be converted  to a list and compared individually.
	stubStr := strings.TrimPrefix(out, expectedContents)
	if !stubDomainsEqual(strings.TrimSpace(stubStr), strings.TrimSpace(expectedStubStr), t) {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"strconv"

	"github.com/1and1/soma/lib/proto"
)

// expects stmt.CheckConfigShowBase as prepared statement
func exportCheckConfig(prepStmt *sql.Stmt, queryId string) (
	*proto.CheckConfig, error) {

	var (
		checkConfigId, repositoryId, checkConfigName   string
		objectId, objectType, capabilityId, externalId string
		bucketId                                       string
		isActive, hasInheritance, isChildrenOnly       bool
		isEnabled                                      bool
		bucketIdOrNull                                 sql.NullString
		interval                                       int64
	)

	if err := prepStmt.QueryRow(queryId).Scan(
		&checkConfigId,
		&repositoryId,
		&bucketIdOrNull,
		&checkConfigName,
		&objectId,
		&objectType,
		&isActive,
		&hasInheritance,
		&isChildrenOnly,
		&capabilityId,
		&interval,
		&isEnabled,
		&externalId,
	); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	if bucketIdOrNull.Valid {
		bucketId = bucketIdOrNull.String
	}

	checkConfig := proto.CheckConfig{
		Id:           checkConfigId,
		Name:         checkConfigName,
		Interval:     uint64(interval),
		RepositoryId: repositoryId,
		BucketId:     bucketId,
		CapabilityId: capabilityId,
		ObjectId:     objectId,
		ObjectType:   objectType,
		IsActive:     isActive,
		IsEnabled:    isEnabled,
		Inheritance:  hasInheritance,
		ChildrenOnly: isChildrenOnly,
		ExternalId:   externalId,
	}
	return &checkConfig, nil
}

// expects stmt.CheckConfigShowThreshold as prepared statement
func exportCheckConfigThresholds(prepStmt *sql.Stmt, queryId string) (
	[]proto.CheckConfigThreshold, error) {

	var (
		err                                       error
		rows                                      *sql.Rows
		checkConfigId, predicateSymbol, threshold string
		levelName, levelShortName                 string
		levelNumeric, thresholdValue              int64
	)
	thresholds := make([]proto.CheckConfigThreshold, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&predicateSymbol,
			&threshold,
			&levelName,
			&levelShortName,
			&levelNumeric,
		); err != nil {
			rows.Close()
			return nil, err
		}
		thresholdValue, _ = strconv.ParseInt(threshold, 10, 64)

		thr := proto.CheckConfigThreshold{
			Predicate: proto.Predicate{
				Symbol: predicateSymbol,
			},
			Level: proto.Level{
				Name:      levelName,
				ShortName: levelShortName,
				Numeric:   uint16(levelNumeric),
			},
			Value: thresholdValue,
		}
		thresholds = append(thresholds, thr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return thresholds, nil
}

// expects in that order:
// - stmt.CheckConfigShowConstrCustom
// - stmt.CheckConfigShowConstrSystem
// - stmt.CheckConfigShowConstrNative
// - stmt.CheckConfigShowConstrService
// - stmt.CheckConfigShowConstrAttribute
// - stmt.CheckConfigShowConstrOncall
func exportCheckConfigConstraints(stmtCustom, stmtSystem,
	stmtNative, stmtService, stmtAttribute, stmtOncall *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var err error
	constraints := make([]proto.CheckConfigConstraint, 0)
	for _, cstrType := range []string{
		`custom`,
		`system`,
		`native`,
		`service`,
		`attribute`,
		`oncall`,
	} {
		cstr := []proto.CheckConfigConstraint{}
		switch cstrType {
		case `custom`:
			cstr, err = exportCheckConfigCustomConstraints(
				stmtCustom, queryId)
		case `system`:
			cstr, err = exportCheckConfigSystemConstraints(
				stmtSystem, queryId)
		case `native`:
			cstr, err = exportCheckConfigNativeConstraints(
				stmtNative, queryId)
		case `service`:
			cstr, err = exportCheckConfigServiceConstraints(
				stmtService, queryId)
		case `attribute`:
			cstr, err = exportCheckConfigAttributeConstraints(
				stmtAttribute, queryId)
		case `oncall`:
			cstr, err = exportCheckConfigOncallConstraints(
				stmtOncall, queryId)
		}
		if err != nil {
			return nil, err
		}
		constraints = append(constraints, cstr...)
	}
	return constraints, nil
}

// expects stmt.CheckConfigShowConstrCustom as prepared statement
func exportCheckConfigCustomConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, propertyId, repositoryId string
		name, value                             string
		rows                                    *sql.Rows
		err                                     error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&propertyId,
			&repositoryId,
			&value,
			&name,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `custom`,
			Custom: &proto.PropertyCustom{
				Id:           propertyId,
				RepositoryId: repositoryId,
				Name:         name,
				Value:        value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrSystem as prepared statement
func exportCheckConfigSystemConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, value string
		rows                       *sql.Rows
		err                        error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&name,
			&value,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `system`,
			System: &proto.PropertySystem{
				Name:  name,
				Value: value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrNative as prepared statement
func exportCheckConfigNativeConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, value string
		rows                       *sql.Rows
		err                        error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&name,
			&value,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `native`,
			Native: &proto.PropertyNative{
				Name:  name,
				Value: value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrService as prepared statement
func exportCheckConfigServiceConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, teamId string
		rows                        *sql.Rows
		err                         error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&teamId,
			&name,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `service`,
			Service: &proto.PropertyService{
				Name:   name,
				TeamId: teamId,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrAttribute as prepared statement
func exportCheckConfigAttributeConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, value string
		rows                       *sql.Rows
		err                        error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&name,
			&value,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `attribute`,
			Attribute: &proto.ServiceAttribute{
				Name:  name,
				Value: value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrOncall as prepared statement
func exportCheckConfigOncallConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, oncallId, name, number string
		rows                                  *sql.Rows
		err                                   error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&oncallId,
			&name,
			&number,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `oncall`,
			Oncall: &proto.PropertyOncall{
				Id:     oncallId,
				Name:   name,
				Number: number,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add functions to export instances for CheckConfig<commit_after>package main

import (
	"database/sql"
	"strconv"

	"github.com/1and1/soma/lib/proto"
)

// expects stmt.CheckConfigShowBase as prepared statement
func exportCheckConfig(prepStmt *sql.Stmt, queryId string) (
	*proto.CheckConfig, error) {

	var (
		checkConfigId, repositoryId, checkConfigName   string
		objectId, objectType, capabilityId, externalId string
		bucketId                                       string
		isActive, hasInheritance, isChildrenOnly       bool
		isEnabled                                      bool
		bucketIdOrNull                                 sql.NullString
		interval                                       int64
	)

	if err := prepStmt.QueryRow(queryId).Scan(
		&checkConfigId,
		&repositoryId,
		&bucketIdOrNull,
		&checkConfigName,
		&objectId,
		&objectType,
		&isActive,
		&hasInheritance,
		&isChildrenOnly,
		&capabilityId,
		&interval,
		&isEnabled,
		&externalId,
	); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	if bucketIdOrNull.Valid {
		bucketId = bucketIdOrNull.String
	}

	checkConfig := proto.CheckConfig{
		Id:           checkConfigId,
		Name:         checkConfigName,
		Interval:     uint64(interval),
		RepositoryId: repositoryId,
		BucketId:     bucketId,
		CapabilityId: capabilityId,
		ObjectId:     objectId,
		ObjectType:   objectType,
		IsActive:     isActive,
		IsEnabled:    isEnabled,
		Inheritance:  hasInheritance,
		ChildrenOnly: isChildrenOnly,
		ExternalId:   externalId,
	}
	return &checkConfig, nil
}

// expects stmt.CheckConfigShowThreshold as prepared statement
func exportCheckConfigThresholds(prepStmt *sql.Stmt, queryId string) (
	[]proto.CheckConfigThreshold, error) {

	var (
		err                                       error
		rows                                      *sql.Rows
		checkConfigId, predicateSymbol, threshold string
		levelName, levelShortName                 string
		levelNumeric, thresholdValue              int64
	)
	thresholds := make([]proto.CheckConfigThreshold, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&predicateSymbol,
			&threshold,
			&levelName,
			&levelShortName,
			&levelNumeric,
		); err != nil {
			rows.Close()
			return nil, err
		}
		thresholdValue, _ = strconv.ParseInt(threshold, 10, 64)

		thr := proto.CheckConfigThreshold{
			Predicate: proto.Predicate{
				Symbol: predicateSymbol,
			},
			Level: proto.Level{
				Name:      levelName,
				ShortName: levelShortName,
				Numeric:   uint16(levelNumeric),
			},
			Value: thresholdValue,
		}
		thresholds = append(thresholds, thr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return thresholds, nil
}

// expects in that order:
// - stmt.CheckConfigShowConstrCustom
// - stmt.CheckConfigShowConstrSystem
// - stmt.CheckConfigShowConstrNative
// - stmt.CheckConfigShowConstrService
// - stmt.CheckConfigShowConstrAttribute
// - stmt.CheckConfigShowConstrOncall
func exportCheckConfigConstraints(stmtCustom, stmtSystem,
	stmtNative, stmtService, stmtAttribute, stmtOncall *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var err error
	constraints := make([]proto.CheckConfigConstraint, 0)
	for _, cstrType := range []string{
		`custom`,
		`system`,
		`native`,
		`service`,
		`attribute`,
		`oncall`,
	} {
		cstr := []proto.CheckConfigConstraint{}
		switch cstrType {
		case `custom`:
			cstr, err = exportCheckConfigCustomConstraints(
				stmtCustom, queryId)
		case `system`:
			cstr, err = exportCheckConfigSystemConstraints(
				stmtSystem, queryId)
		case `native`:
			cstr, err = exportCheckConfigNativeConstraints(
				stmtNative, queryId)
		case `service`:
			cstr, err = exportCheckConfigServiceConstraints(
				stmtService, queryId)
		case `attribute`:
			cstr, err = exportCheckConfigAttributeConstraints(
				stmtAttribute, queryId)
		case `oncall`:
			cstr, err = exportCheckConfigOncallConstraints(
				stmtOncall, queryId)
		}
		if err != nil {
			return nil, err
		}
		constraints = append(constraints, cstr...)
	}
	return constraints, nil
}

// expects stmt.CheckConfigShowConstrCustom as prepared statement
func exportCheckConfigCustomConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, propertyId, repositoryId string
		name, value                             string
		rows                                    *sql.Rows
		err                                     error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&propertyId,
			&repositoryId,
			&value,
			&name,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `custom`,
			Custom: &proto.PropertyCustom{
				Id:           propertyId,
				RepositoryId: repositoryId,
				Name:         name,
				Value:        value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrSystem as prepared statement
func exportCheckConfigSystemConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, value string
		rows                       *sql.Rows
		err                        error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&name,
			&value,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `system`,
			System: &proto.PropertySystem{
				Name:  name,
				Value: value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrNative as prepared statement
func exportCheckConfigNativeConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, value string
		rows                       *sql.Rows
		err                        error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&name,
			&value,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `native`,
			Native: &proto.PropertyNative{
				Name:  name,
				Value: value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrService as prepared statement
func exportCheckConfigServiceConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, teamId string
		rows                        *sql.Rows
		err                         error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&teamId,
			&name,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `service`,
			Service: &proto.PropertyService{
				Name:   name,
				TeamId: teamId,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrAttribute as prepared statement
func exportCheckConfigAttributeConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, name, value string
		rows                       *sql.Rows
		err                        error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&name,
			&value,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `attribute`,
			Attribute: &proto.ServiceAttribute{
				Name:  name,
				Value: value,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigShowConstrOncall as prepared statement
func exportCheckConfigOncallConstraints(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckConfigConstraint, error) {

	var (
		checkConfigId, oncallId, name, number string
		rows                                  *sql.Rows
		err                                   error
	)

	constraints := make([]proto.CheckConfigConstraint, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&checkConfigId,
			&oncallId,
			&name,
			&number,
		); err != nil {
			rows.Close()
			return nil, err
		}
		cstr := proto.CheckConfigConstraint{
			ConstraintType: `oncall`,
			Oncall: &proto.PropertyOncall{
				Id:     oncallId,
				Name:   name,
				Number: number,
			},
		}
		constraints = append(constraints, cstr)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return constraints, nil
}

// expects stmt.CheckConfigInstanceInfo as prepared statement
func exportCheckInstancesForConfig(prepStmt *sql.Stmt,
	queryId string) ([]proto.CheckInstanceInfo, error) {

	var (
		err  error
		rows *sql.Rows
	)

	instances := make([]proto.CheckInstanceInfo, 0)

	if rows, err = prepStmt.Query(queryId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&instanceId,
			&objectId,
			&objectType,
			&currentStatus,
			&nextStatus,
		); err != nil {
			rows.Close()
			return nil, err
		}
		info := proto.CheckInstanceInfo{
			Id:            instanceId,
			ObjectId:      objectId,
			ObjectType:    objectType,
			CurrentStatus: currentStatus,
			NextStatus:    nextStatus,
		}
		instances = append(instances, info)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return instances, nil
}

// expects stmt.CheckConfigObjectInstanceInfo as prepared statement
func exportCheckInstancesForObject(prepStmt *sql.Stmt,
	configId, objectId string) ([]proto.CheckInstanceInfo, error) {

	var (
		err  error
		rows *sql.Rows
	)

	instances := make([]proto.CheckInstanceInfo, 0)

	if rows, err = prepStmt.Query(configId, objectId); err != nil {
		return nil, err
	}

	for rows.Next() {
		if err = rows.Scan(
			&instanceId,
			&objectId,
			&objectType,
			&currentStatus,
			&nextStatus,
		); err != nil {
			rows.Close()
			return nil, err
		}
		info := proto.CheckInstanceInfo{
			Id:            instanceId,
			ObjectId:      objectId,
			ObjectType:    objectType,
			CurrentStatus: currentStatus,
			NextStatus:    nextStatus,
		}
		instances = append(instances, info)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return instances, nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


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

/*
	Package fmt implements formatted I/O with functions analogous
	to C's printf and scanf.  The format 'verbs' are derived from C's but
	are simpler.

	Printing:

	The verbs:

	General:
		%v	the value in a default format.
			when printing structs, the plus flag (%+v) adds field names
		%#v	a Go-syntax representation of the value
		%T	a Go-syntax representation of the type of the value

	Boolean:
		%t	the word true or false
	Integer:
		%b	base 2
		%c	the character represented by the corresponding Unicode code point
		%d	base 10
		%o	base 8
		%x	base 16, with lower-case letters for a-f
		%X	base 16, with upper-case letters for A-F
		%U	Unicode format: U+1234; same as "U+%x" with 4 digits default
	Floating-point and complex constituents:
		%e	scientific notation, e.g. -1234.456e+78
		%E	scientific notation, e.g. -1234.456E+78
		%f	decimal point but no exponent, e.g. 123.456
		%g	whichever of %e or %f produces more compact output
		%G	whichever of %E or %f produces more compact output
	String and slice of bytes:
		%s	the uninterpreted bytes of the string or slice
		%q	a double-quoted string safely escaped with Go syntax
		%x	base 16, lower-case, two characters per byte
		%X	base 16, upper-case, two characters per byte
	Pointer:
		%p	base 16 notation, with leading 0x

	There is no 'u' flag.  Integers are printed unsigned if they have unsigned type.
	Similarly, there is no need to specify the size of the operand (int8, int64).

	The width and precision control formatting and are in units of Unicode
	code points.  (This differs from C's printf where the units are numbers
	of bytes.) Either or both of the flags may be replaced with the
	character '*', causing their values to be obtained from the next
	operand, which must be of type int.

	For numeric values, width sets the width of the field and precision
	sets the number of places after the decimal, if appropriate.  For
	example, the format %6.2f prints 123.45.

	For strings, width is the minimum number of characters to output,
	padding with spaces if necessary, and precision is the maximum
	number of characters to output, truncating if necessary.

	Other flags:
		+	always print a sign for numeric values
		-	pad with spaces on the right rather than the left (left-justify the field)
		#	alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
			0X for hex (%#X); suppress 0x for %p (%#p);
			print a raw (backquoted) string if possible for %q (%#q)
		' '	(space) leave a space for elided sign in numbers (% d);
			put spaces between bytes printing strings or slices in hex (% x, % X)
		0	pad with leading zeros rather than spaces

	For each Printf-like function, there is also a Print function
	that takes no format and is equivalent to saying %v for every
	operand.  Another variant Println inserts blanks between
	operands and appends a newline.

	Regardless of the verb, if an operand is an interface value,
	the internal concrete value is used, not the interface itself.
	Thus:
		var i interface{} = 23
		fmt.Printf("%v\n", i)
	will print 23.

	If an operand implements interface Formatter, that interface
	can be used for fine control of formatting.

	If an operand implements method String() string that method
	will be used to convert the object to a string, which will then
	be formatted as required by the verb (if any). To avoid
	recursion in cases such as
		type X int
		func (x X) String() string { return Sprintf("%d", x) }
	cast the value before recurring:
		func (x X) String() string { return Sprintf("%d", int(x)) }

	Format errors:

	If an invalid argument is given for a verb, such as providing
	a string to %d, the generated string will contain a
	description of the problem, as in these examples:

		Wrong type or unknown verb: %!verb(type=value)
			Printf("%d", hi):          %!d(string=hi)
		Too many arguments: %!(EXTRA type=value)
			Printf("hi", "guys"):      hi%!(EXTRA string=guys)
		Too few arguments: %!verb(MISSING)
			Printf("hi%d"):            hi %!d(MISSING)
		Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
			Printf("%*s", 4.5, "hi"):  %!(BADWIDTH)hi
			Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi

	All errors begin with the string "%!" followed sometimes
	by a single character (the verb) and end with a parenthesized
	description.

	Scanning:

	An analogous set of functions scans formatted text to yield
	values.  Scan, Scanf and Scanln read from os.Stdin; Fscan,
	Fscanf and Fscanln read from a specified os.Reader; Sscan,
	Sscanf and Sscanln read from an argument string.  Sscanln,
	Fscanln and Sscanln stop scanning at a newline and require that
	the items be followed by one; Sscanf, Fscanf and Sscanf require
	newlines in the input to match newlines in the format; the other
	routines treat newlines as spaces.

	Scanf, Fscanf, and Sscanf parse the arguments according to a
	format string, analogous to that of Printf.  For example, %x
	will scan an integer as a hexadecimal number, and %v will scan
	the default representation format for the value.

	The formats behave analogously to those of Printf with the
	following exceptions:

	%p is not implemented
	%T is not implemented
	%e %E %f %F %g %g are all equivalent and scan any floating point or complex value
	%s and %v on strings scan a space-delimited token

	Width is interpreted in the input text (%5s means at most
	five runes of input will be read to scan a string) but there
	is no syntax for scanning with a precision (no %5.2f, just
	%5f).

	When scanning with a format, all non-empty runs of space
	characters (except newline) are equivalent to a single
	space in both the format and the input.  With that proviso,
	text in the format string must match the input text; scanning
	stops if it does not, with the return value of the function
	indicating the number of arguments scanned.

	In all the scanning functions, if an operand implements method
	Scan (that is, it implements the Scanner interface) that
	method will be used to scan the text for that operand.  Also,
	if the number of arguments scanned is less than the number of
	arguments provided, an error is returned.

	All arguments to be scanned must be either pointers to basic
	types or implementations of the Scanner interface.

	Note: Fscan etc. can read one character (rune) past the
	input they return, which means that a loop calling a scan
	routine may skip some of the input.  This is usually a
	problem only when there is no space between input values.
	However, if the reader provided to Fscan implements UnreadRune,
	that method will be used to save the character and successive
	calls will not lose data.  To attach an UnreadRune method
	to a reader without that capability, use bufio.NewReader.
*/
package fmt
<commit_msg>fmt: document %b for floating point<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
	Package fmt implements formatted I/O with functions analogous
	to C's printf and scanf.  The format 'verbs' are derived from C's but
	are simpler.

	Printing:

	The verbs:

	General:
		%v	the value in a default format.
			when printing structs, the plus flag (%+v) adds field names
		%#v	a Go-syntax representation of the value
		%T	a Go-syntax representation of the type of the value

	Boolean:
		%t	the word true or false
	Integer:
		%b	base 2
		%c	the character represented by the corresponding Unicode code point
		%d	base 10
		%o	base 8
		%x	base 16, with lower-case letters for a-f
		%X	base 16, with upper-case letters for A-F
		%U	Unicode format: U+1234; same as "U+%x" with 4 digits default
	Floating-point and complex constituents:
		%b	decimalless scientific notation with exponent a power
			of two, in the manner of strconv.Ftoa32, e.g. -123456p-78
		%e	scientific notation, e.g. -1234.456e+78
		%E	scientific notation, e.g. -1234.456E+78
		%f	decimal point but no exponent, e.g. 123.456
		%g	whichever of %e or %f produces more compact output
		%G	whichever of %E or %f produces more compact output
	String and slice of bytes:
		%s	the uninterpreted bytes of the string or slice
		%q	a double-quoted string safely escaped with Go syntax
		%x	base 16, lower-case, two characters per byte
		%X	base 16, upper-case, two characters per byte
	Pointer:
		%p	base 16 notation, with leading 0x

	There is no 'u' flag.  Integers are printed unsigned if they have unsigned type.
	Similarly, there is no need to specify the size of the operand (int8, int64).

	The width and precision control formatting and are in units of Unicode
	code points.  (This differs from C's printf where the units are numbers
	of bytes.) Either or both of the flags may be replaced with the
	character '*', causing their values to be obtained from the next
	operand, which must be of type int.

	For numeric values, width sets the width of the field and precision
	sets the number of places after the decimal, if appropriate.  For
	example, the format %6.2f prints 123.45.

	For strings, width is the minimum number of characters to output,
	padding with spaces if necessary, and precision is the maximum
	number of characters to output, truncating if necessary.

	Other flags:
		+	always print a sign for numeric values
		-	pad with spaces on the right rather than the left (left-justify the field)
		#	alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
			0X for hex (%#X); suppress 0x for %p (%#p);
			print a raw (backquoted) string if possible for %q (%#q)
		' '	(space) leave a space for elided sign in numbers (% d);
			put spaces between bytes printing strings or slices in hex (% x, % X)
		0	pad with leading zeros rather than spaces

	For each Printf-like function, there is also a Print function
	that takes no format and is equivalent to saying %v for every
	operand.  Another variant Println inserts blanks between
	operands and appends a newline.

	Regardless of the verb, if an operand is an interface value,
	the internal concrete value is used, not the interface itself.
	Thus:
		var i interface{} = 23
		fmt.Printf("%v\n", i)
	will print 23.

	If an operand implements interface Formatter, that interface
	can be used for fine control of formatting.

	If an operand implements method String() string that method
	will be used to convert the object to a string, which will then
	be formatted as required by the verb (if any). To avoid
	recursion in cases such as
		type X int
		func (x X) String() string { return Sprintf("%d", x) }
	cast the value before recurring:
		func (x X) String() string { return Sprintf("%d", int(x)) }

	Format errors:

	If an invalid argument is given for a verb, such as providing
	a string to %d, the generated string will contain a
	description of the problem, as in these examples:

		Wrong type or unknown verb: %!verb(type=value)
			Printf("%d", hi):          %!d(string=hi)
		Too many arguments: %!(EXTRA type=value)
			Printf("hi", "guys"):      hi%!(EXTRA string=guys)
		Too few arguments: %!verb(MISSING)
			Printf("hi%d"):            hi %!d(MISSING)
		Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
			Printf("%*s", 4.5, "hi"):  %!(BADWIDTH)hi
			Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi

	All errors begin with the string "%!" followed sometimes
	by a single character (the verb) and end with a parenthesized
	description.

	Scanning:

	An analogous set of functions scans formatted text to yield
	values.  Scan, Scanf and Scanln read from os.Stdin; Fscan,
	Fscanf and Fscanln read from a specified os.Reader; Sscan,
	Sscanf and Sscanln read from an argument string.  Sscanln,
	Fscanln and Sscanln stop scanning at a newline and require that
	the items be followed by one; Sscanf, Fscanf and Sscanf require
	newlines in the input to match newlines in the format; the other
	routines treat newlines as spaces.

	Scanf, Fscanf, and Sscanf parse the arguments according to a
	format string, analogous to that of Printf.  For example, %x
	will scan an integer as a hexadecimal number, and %v will scan
	the default representation format for the value.

	The formats behave analogously to those of Printf with the
	following exceptions:

	%p is not implemented
	%T is not implemented
	%e %E %f %F %g %g are all equivalent and scan any floating point or complex value
	%s and %v on strings scan a space-delimited token

	Width is interpreted in the input text (%5s means at most
	five runes of input will be read to scan a string) but there
	is no syntax for scanning with a precision (no %5.2f, just
	%5f).

	When scanning with a format, all non-empty runs of space
	characters (except newline) are equivalent to a single
	space in both the format and the input.  With that proviso,
	text in the format string must match the input text; scanning
	stops if it does not, with the return value of the function
	indicating the number of arguments scanned.

	In all the scanning functions, if an operand implements method
	Scan (that is, it implements the Scanner interface) that
	method will be used to scan the text for that operand.  Also,
	if the number of arguments scanned is less than the number of
	arguments provided, an error is returned.

	All arguments to be scanned must be either pointers to basic
	types or implementations of the Scanner interface.

	Note: Fscan etc. can read one character (rune) past the
	input they return, which means that a loop calling a scan
	routine may skip some of the input.  This is usually a
	problem only when there is no space between input values.
	However, if the reader provided to Fscan implements UnreadRune,
	that method will be used to save the character and successive
	calls will not lose data.  To attach an UnreadRune method
	to a reader without that capability, use bufio.NewReader.
*/
package fmt
<|endoftext|>


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

/*
Package net provides a portable interface for network I/O, including
TCP/IP, UDP, domain name resolution, and Unix domain sockets.

Although the package provides access to low-level networking
primitives, most clients will need only the basic interface provided
by the Dial, Listen, and Accept functions and the associated
Conn and Listener interfaces. The crypto/tls package uses
the same interfaces and similar Dial and Listen functions.

The Dial function connects to a server:

	conn, err := net.Dial("tcp", "google.com:80")
	if err != nil {
		// handle error
	}
	fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
	status, err := bufio.NewReader(conn).ReadString('\n')
	// ...

The Listen function creates servers:

	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		// handle error
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			// handle error
			continue
		}
		go handleConnection(conn)
	}
*/
package net

// TODO(rsc):
//	support for raw ethernet sockets

import (
	"errors"
	"io"
	"os"
	"sync"
	"syscall"
	"time"
)

// Addr represents a network end point address.
type Addr interface {
	Network() string // name of the network
	String() string  // string form of address
}

// Conn is a generic stream-oriented network connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
type Conn interface {
	// Read reads data from the connection.
	// Read can be made to time out and return a Error with Timeout() == true
	// after a fixed time limit; see SetDeadline and SetReadDeadline.
	Read(b []byte) (n int, err error)

	// Write writes data to the connection.
	// Write can be made to time out and return a Error with Timeout() == true
	// after a fixed time limit; see SetDeadline and SetWriteDeadline.
	Write(b []byte) (n int, err error)

	// Close closes the connection.
	// Any blocked Read or Write operations will be unblocked and return errors.
	Close() error

	// LocalAddr returns the local network address.
	LocalAddr() Addr

	// RemoteAddr returns the remote network address.
	RemoteAddr() Addr

	// SetDeadline sets the read and write deadlines associated
	// with the connection. It is equivalent to calling both
	// SetReadDeadline and SetWriteDeadline.
	//
	// A deadline is an absolute time after which I/O operations
	// fail with a timeout (see type Error) instead of
	// blocking. The deadline applies to all future I/O, not just
	// the immediately following call to Read or Write.
	//
	// An idle timeout can be implemented by repeatedly extending
	// the deadline after successful Read or Write calls.
	//
	// A zero value for t means I/O operations will not time out.
	SetDeadline(t time.Time) error

	// SetReadDeadline sets the deadline for future Read calls.
	// A zero value for t means Read will not time out.
	SetReadDeadline(t time.Time) error

	// SetWriteDeadline sets the deadline for future Write calls.
	// Even if write times out, it may return n > 0, indicating that
	// some of the data was successfully written.
	// A zero value for t means Write will not time out.
	SetWriteDeadline(t time.Time) error
}

type conn struct {
	fd *netFD
}

func (c *conn) ok() bool { return c != nil && c.fd != nil }

// Implementation of the Conn interface.

// Read implements the Conn Read method.
func (c *conn) Read(b []byte) (int, error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Read(b)
}

// Write implements the Conn Write method.
func (c *conn) Write(b []byte) (int, error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Write(b)
}

// Close closes the connection.
func (c *conn) Close() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.Close()
}

// LocalAddr returns the local network address.
func (c *conn) LocalAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.laddr
}

// RemoteAddr returns the remote network address.
func (c *conn) RemoteAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.raddr
}

// SetDeadline implements the Conn SetDeadline method.
func (c *conn) SetDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setDeadline(c.fd, t)
}

// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *conn) SetReadDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadDeadline(c.fd, t)
}

// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *conn) SetWriteDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteDeadline(c.fd, t)
}

// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
func (c *conn) SetReadBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadBuffer(c.fd, bytes)
}

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *conn) SetWriteBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteBuffer(c.fd, bytes)
}

// File sets the underlying os.File to blocking mode and returns a copy.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
//
// The returned os.File's file descriptor is different from the connection's.
// Attempting to change properties of the original using this duplicate
// may or may not have the desired effect.
func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }

// An Error represents a network error.
type Error interface {
	error
	Timeout() bool   // Is the error a timeout?
	Temporary() bool // Is the error temporary?
}

// PacketConn is a generic packet-oriented network connection.
//
// Multiple goroutines may invoke methods on a PacketConn simultaneously.
type PacketConn interface {
	// ReadFrom reads a packet from the connection,
	// copying the payload into b.  It returns the number of
	// bytes copied into b and the return address that
	// was on the packet.
	// ReadFrom can be made to time out and return
	// an error with Timeout() == true after a fixed time limit;
	// see SetDeadline and SetReadDeadline.
	ReadFrom(b []byte) (n int, addr Addr, err error)

	// WriteTo writes a packet with payload b to addr.
	// WriteTo can be made to time out and return
	// an error with Timeout() == true after a fixed time limit;
	// see SetDeadline and SetWriteDeadline.
	// On packet-oriented connections, write timeouts are rare.
	WriteTo(b []byte, addr Addr) (n int, err error)

	// Close closes the connection.
	// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
	Close() error

	// LocalAddr returns the local network address.
	LocalAddr() Addr

	// SetDeadline sets the read and write deadlines associated
	// with the connection.
	SetDeadline(t time.Time) error

	// SetReadDeadline sets the deadline for future Read calls.
	// If the deadline is reached, Read will fail with a timeout
	// (see type Error) instead of blocking.
	// A zero value for t means Read will not time out.
	SetReadDeadline(t time.Time) error

	// SetWriteDeadline sets the deadline for future Write calls.
	// If the deadline is reached, Write will fail with a timeout
	// (see type Error) instead of blocking.
	// A zero value for t means Write will not time out.
	// Even if write times out, it may return n > 0, indicating that
	// some of the data was successfully written.
	SetWriteDeadline(t time.Time) error
}

// A Listener is a generic network listener for stream-oriented protocols.
//
// Multiple goroutines may invoke methods on a Listener simultaneously.
type Listener interface {
	// Accept waits for and returns the next connection to the listener.
	Accept() (c Conn, err error)

	// Close closes the listener.
	// Any blocked Accept operations will be unblocked and return errors.
	Close() error

	// Addr returns the listener's network address.
	Addr() Addr
}

var errMissingAddress = errors.New("missing address")

type OpError struct {
	Op   string
	Net  string
	Addr Addr
	Err  error
}

func (e *OpError) Error() string {
	if e == nil {
		return "<nil>"
	}
	s := e.Op
	if e.Net != "" {
		s += " " + e.Net
	}
	if e.Addr != nil {
		s += " " + e.Addr.String()
	}
	s += ": " + e.Err.Error()
	return s
}

type temporary interface {
	Temporary() bool
}

func (e *OpError) Temporary() bool {
	t, ok := e.Err.(temporary)
	return ok && t.Temporary()
}

var noDeadline = time.Time{}

type timeout interface {
	Timeout() bool
}

func (e *OpError) Timeout() bool {
	t, ok := e.Err.(timeout)
	return ok && t.Timeout()
}

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }

var errTimeout error = &timeoutError{}

var errClosing = errors.New("use of closed network connection")

type AddrError struct {
	Err  string
	Addr string
}

func (e *AddrError) Error() string {
	if e == nil {
		return "<nil>"
	}
	s := e.Err
	if e.Addr != "" {
		s += " " + e.Addr
	}
	return s
}

func (e *AddrError) Temporary() bool {
	return false
}

func (e *AddrError) Timeout() bool {
	return false
}

type UnknownNetworkError string

func (e UnknownNetworkError) Error() string   { return "unknown network " + string(e) }
func (e UnknownNetworkError) Temporary() bool { return false }
func (e UnknownNetworkError) Timeout() bool   { return false }

// DNSConfigError represents an error reading the machine's DNS configuration.
type DNSConfigError struct {
	Err error
}

func (e *DNSConfigError) Error() string {
	return "error reading DNS config: " + e.Err.Error()
}

func (e *DNSConfigError) Timeout() bool   { return false }
func (e *DNSConfigError) Temporary() bool { return false }

type writerOnly struct {
	io.Writer
}

// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
// applicable.
func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
	// Use wrapper to hide existing r.ReadFrom from io.Copy.
	return io.Copy(writerOnly{w}, r)
}

// deadline is an atomically-accessed number of nanoseconds since 1970
// or 0, if no deadline is set.
type deadline struct {
	sync.Mutex
	val int64
}

func (d *deadline) expired() bool {
	t := d.value()
	return t > 0 && time.Now().UnixNano() >= t
}

func (d *deadline) value() (v int64) {
	d.Lock()
	v = d.val
	d.Unlock()
	return
}

func (d *deadline) set(v int64) {
	d.Lock()
	d.val = v
	d.Unlock()
}

func (d *deadline) setTime(t time.Time) {
	if t.IsZero() {
		d.set(0)
	} else {
		d.set(t.UnixNano())
	}
}
<commit_msg>net: document OpError<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package net provides a portable interface for network I/O, including
TCP/IP, UDP, domain name resolution, and Unix domain sockets.

Although the package provides access to low-level networking
primitives, most clients will need only the basic interface provided
by the Dial, Listen, and Accept functions and the associated
Conn and Listener interfaces. The crypto/tls package uses
the same interfaces and similar Dial and Listen functions.

The Dial function connects to a server:

	conn, err := net.Dial("tcp", "google.com:80")
	if err != nil {
		// handle error
	}
	fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
	status, err := bufio.NewReader(conn).ReadString('\n')
	// ...

The Listen function creates servers:

	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		// handle error
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			// handle error
			continue
		}
		go handleConnection(conn)
	}
*/
package net

// TODO(rsc):
//	support for raw ethernet sockets

import (
	"errors"
	"io"
	"os"
	"sync"
	"syscall"
	"time"
)

// Addr represents a network end point address.
type Addr interface {
	Network() string // name of the network
	String() string  // string form of address
}

// Conn is a generic stream-oriented network connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
type Conn interface {
	// Read reads data from the connection.
	// Read can be made to time out and return a Error with Timeout() == true
	// after a fixed time limit; see SetDeadline and SetReadDeadline.
	Read(b []byte) (n int, err error)

	// Write writes data to the connection.
	// Write can be made to time out and return a Error with Timeout() == true
	// after a fixed time limit; see SetDeadline and SetWriteDeadline.
	Write(b []byte) (n int, err error)

	// Close closes the connection.
	// Any blocked Read or Write operations will be unblocked and return errors.
	Close() error

	// LocalAddr returns the local network address.
	LocalAddr() Addr

	// RemoteAddr returns the remote network address.
	RemoteAddr() Addr

	// SetDeadline sets the read and write deadlines associated
	// with the connection. It is equivalent to calling both
	// SetReadDeadline and SetWriteDeadline.
	//
	// A deadline is an absolute time after which I/O operations
	// fail with a timeout (see type Error) instead of
	// blocking. The deadline applies to all future I/O, not just
	// the immediately following call to Read or Write.
	//
	// An idle timeout can be implemented by repeatedly extending
	// the deadline after successful Read or Write calls.
	//
	// A zero value for t means I/O operations will not time out.
	SetDeadline(t time.Time) error

	// SetReadDeadline sets the deadline for future Read calls.
	// A zero value for t means Read will not time out.
	SetReadDeadline(t time.Time) error

	// SetWriteDeadline sets the deadline for future Write calls.
	// Even if write times out, it may return n > 0, indicating that
	// some of the data was successfully written.
	// A zero value for t means Write will not time out.
	SetWriteDeadline(t time.Time) error
}

type conn struct {
	fd *netFD
}

func (c *conn) ok() bool { return c != nil && c.fd != nil }

// Implementation of the Conn interface.

// Read implements the Conn Read method.
func (c *conn) Read(b []byte) (int, error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Read(b)
}

// Write implements the Conn Write method.
func (c *conn) Write(b []byte) (int, error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	return c.fd.Write(b)
}

// Close closes the connection.
func (c *conn) Close() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.Close()
}

// LocalAddr returns the local network address.
func (c *conn) LocalAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.laddr
}

// RemoteAddr returns the remote network address.
func (c *conn) RemoteAddr() Addr {
	if !c.ok() {
		return nil
	}
	return c.fd.raddr
}

// SetDeadline implements the Conn SetDeadline method.
func (c *conn) SetDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setDeadline(c.fd, t)
}

// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *conn) SetReadDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadDeadline(c.fd, t)
}

// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *conn) SetWriteDeadline(t time.Time) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteDeadline(c.fd, t)
}

// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
func (c *conn) SetReadBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setReadBuffer(c.fd, bytes)
}

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *conn) SetWriteBuffer(bytes int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setWriteBuffer(c.fd, bytes)
}

// File sets the underlying os.File to blocking mode and returns a copy.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
//
// The returned os.File's file descriptor is different from the connection's.
// Attempting to change properties of the original using this duplicate
// may or may not have the desired effect.
func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }

// An Error represents a network error.
type Error interface {
	error
	Timeout() bool   // Is the error a timeout?
	Temporary() bool // Is the error temporary?
}

// PacketConn is a generic packet-oriented network connection.
//
// Multiple goroutines may invoke methods on a PacketConn simultaneously.
type PacketConn interface {
	// ReadFrom reads a packet from the connection,
	// copying the payload into b.  It returns the number of
	// bytes copied into b and the return address that
	// was on the packet.
	// ReadFrom can be made to time out and return
	// an error with Timeout() == true after a fixed time limit;
	// see SetDeadline and SetReadDeadline.
	ReadFrom(b []byte) (n int, addr Addr, err error)

	// WriteTo writes a packet with payload b to addr.
	// WriteTo can be made to time out and return
	// an error with Timeout() == true after a fixed time limit;
	// see SetDeadline and SetWriteDeadline.
	// On packet-oriented connections, write timeouts are rare.
	WriteTo(b []byte, addr Addr) (n int, err error)

	// Close closes the connection.
	// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
	Close() error

	// LocalAddr returns the local network address.
	LocalAddr() Addr

	// SetDeadline sets the read and write deadlines associated
	// with the connection.
	SetDeadline(t time.Time) error

	// SetReadDeadline sets the deadline for future Read calls.
	// If the deadline is reached, Read will fail with a timeout
	// (see type Error) instead of blocking.
	// A zero value for t means Read will not time out.
	SetReadDeadline(t time.Time) error

	// SetWriteDeadline sets the deadline for future Write calls.
	// If the deadline is reached, Write will fail with a timeout
	// (see type Error) instead of blocking.
	// A zero value for t means Write will not time out.
	// Even if write times out, it may return n > 0, indicating that
	// some of the data was successfully written.
	SetWriteDeadline(t time.Time) error
}

// A Listener is a generic network listener for stream-oriented protocols.
//
// Multiple goroutines may invoke methods on a Listener simultaneously.
type Listener interface {
	// Accept waits for and returns the next connection to the listener.
	Accept() (c Conn, err error)

	// Close closes the listener.
	// Any blocked Accept operations will be unblocked and return errors.
	Close() error

	// Addr returns the listener's network address.
	Addr() Addr
}

var errMissingAddress = errors.New("missing address")

// OpError is the error type usually returned by functions in the net
// package. It describes the operation, network type, and address of
// an error.
type OpError struct {
	// Op is the operation which caused the error, such as
	// "read" or "write".
	Op string

	// Net is the network type on which this error occurred,
	// such as "tcp" or "udp6".
	Net string

	// Addr is the network address on which this error occurred.
	Addr Addr

	// Err is the error that occurred during the operation.
	Err error
}

func (e *OpError) Error() string {
	if e == nil {
		return "<nil>"
	}
	s := e.Op
	if e.Net != "" {
		s += " " + e.Net
	}
	if e.Addr != nil {
		s += " " + e.Addr.String()
	}
	s += ": " + e.Err.Error()
	return s
}

type temporary interface {
	Temporary() bool
}

func (e *OpError) Temporary() bool {
	t, ok := e.Err.(temporary)
	return ok && t.Temporary()
}

var noDeadline = time.Time{}

type timeout interface {
	Timeout() bool
}

func (e *OpError) Timeout() bool {
	t, ok := e.Err.(timeout)
	return ok && t.Timeout()
}

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }

var errTimeout error = &timeoutError{}

var errClosing = errors.New("use of closed network connection")

type AddrError struct {
	Err  string
	Addr string
}

func (e *AddrError) Error() string {
	if e == nil {
		return "<nil>"
	}
	s := e.Err
	if e.Addr != "" {
		s += " " + e.Addr
	}
	return s
}

func (e *AddrError) Temporary() bool {
	return false
}

func (e *AddrError) Timeout() bool {
	return false
}

type UnknownNetworkError string

func (e UnknownNetworkError) Error() string   { return "unknown network " + string(e) }
func (e UnknownNetworkError) Temporary() bool { return false }
func (e UnknownNetworkError) Timeout() bool   { return false }

// DNSConfigError represents an error reading the machine's DNS configuration.
type DNSConfigError struct {
	Err error
}

func (e *DNSConfigError) Error() string {
	return "error reading DNS config: " + e.Err.Error()
}

func (e *DNSConfigError) Timeout() bool   { return false }
func (e *DNSConfigError) Temporary() bool { return false }

type writerOnly struct {
	io.Writer
}

// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
// applicable.
func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
	// Use wrapper to hide existing r.ReadFrom from io.Copy.
	return io.Copy(writerOnly{w}, r)
}

// deadline is an atomically-accessed number of nanoseconds since 1970
// or 0, if no deadline is set.
type deadline struct {
	sync.Mutex
	val int64
}

func (d *deadline) expired() bool {
	t := d.value()
	return t > 0 && time.Now().UnixNano() >= t
}

func (d *deadline) value() (v int64) {
	d.Lock()
	v = d.val
	d.Unlock()
	return
}

func (d *deadline) set(v int64) {
	d.Lock()
	d.val = v
	d.Unlock()
}

func (d *deadline) setTime(t time.Time) {
	if t.IsZero() {
		d.set(0)
	} else {
		d.set(t.UnixNano())
	}
}
<|endoftext|>


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

// Process etc.

package os

import "syscall"

// Args is the command-line arguments, starting with the program name.
var Args []string

// Getuid returns the numeric user id of the caller.
func Getuid() int { return syscall.Getuid() }

// Geteuid returns the numeric effective user id of the caller.
func Geteuid() int { return syscall.Geteuid() }

// Getgid returns the numeric group id of the caller.
func Getgid() int { return syscall.Getgid() }

// Getegid returns the numeric effective group id of the caller.
func Getegid() int { return syscall.Getegid() }

// Getgroups returns a list of the numeric ids of groups that the caller belongs to.
func Getgroups() ([]int, error) {
	gids, e := syscall.Getgroups()
	return gids, NewSyscallError("getgroups", e)
}

// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
func Exit(code int) { syscall.Exit(code) }
<commit_msg>os: fix comment per CL 5370091<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Process etc.

package os

import "syscall"

// Args hold the command-line arguments, starting with the program name.
var Args []string

// Getuid returns the numeric user id of the caller.
func Getuid() int { return syscall.Getuid() }

// Geteuid returns the numeric effective user id of the caller.
func Geteuid() int { return syscall.Geteuid() }

// Getgid returns the numeric group id of the caller.
func Getgid() int { return syscall.Getgid() }

// Getegid returns the numeric effective group id of the caller.
func Getegid() int { return syscall.Getegid() }

// Getgroups returns a list of the numeric ids of groups that the caller belongs to.
func Getgroups() ([]int, error) {
	gids, e := syscall.Getgroups()
	return gids, NewSyscallError("getgroups", e)
}

// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
func Exit(code int) { syscall.Exit(code) }
<|endoftext|>


<commit_before>package board

import (
	"color"
	"matrix"
	"piece"
)

func (board Board) IsChecked(c color.Color) bool {
	var king matrix.Point
	for i := 0; i < 8; i++ {
		for j := 0; j < 8; j++ {
			symbol := board.Matrix[i][j]
			if piece.King.IsSymbol(symbol) && color.WhichColor(symbol) == c {
				king = matrix.Point{i, j}
				break
			}
		}
	}
	enemy := c.Enemy()
	for i := 0; i < 8; i++ {
		for j := 0; j < 8; j++ {
			symbol := board.Matrix[i][j]
			if color.WhichColor(symbol) != enemy {
				continue
			}

			from := matrix.Point{i, j}
			piece := piece.WhichPiece(symbol)
			first := board.first[i][j]
			diff := from.Diff(king)
			if piece.CanMove(diff, first, true) && board.Matrix.ExistBarrier(from, king) == false {
				return true
			}
		}
	}
	return false
}

func (board Board) IsCheckMate(c color.Color) bool {
	if board.IsChecked(c) == false {
		return false
	}

	friend := make([]matrix.Point, 16)
	for i := 0; i < 8; i++ {
		for j := 0; j < 8; j++ {
			symbol := board.Matrix[i][j]
			if color.WhichColor(symbol) == c {
				friend = append(friend, matrix.Point{i, j})
			}
		}
	}

	for i := 0; i < 8; i++ {
		for j := 0; j < 8; j++ {
			to := matrix.Point{i, j}
			for _, k := range friend {
				fsymbol := board.Matrix[k.Y][k.X]
				tsymbol := board.Matrix[to.Y][to.X]
				tcolor := color.WhichColor(tsymbol)
				if tcolor == c || tcolor == color.Unknown {
					continue
				}

				piece := piece.WhichPiece(fsymbol)
				first := board.first[k.Y][k.X]
				var diff matrix.Point
				if c == color.White {
					diff = k.Diff(to)
				} else if c == color.Black {
					diff = to.Diff(k)
				}
				toEnemy := color.WhichColor(tsymbol) == c.Enemy()
				if piece.CanMove(diff, first, toEnemy) && board.Matrix.ExistBarrier(k, to) == false {
					board.Matrix[to.Y][to.X] = fsymbol
					board.Matrix[k.Y][k.X] = ' '
					if board.IsChecked(c) == false {
						board.Matrix[to.Y][to.X] = tsymbol
						board.Matrix[k.Y][k.X] = fsymbol
						return false
					}
					board.Matrix[to.Y][to.X] = tsymbol
					board.Matrix[k.Y][k.X] = fsymbol
				}
			}
		}
	}
	return true
}
<commit_msg>Rewrite check check with Matrix.Positions<commit_after>package board

import (
	"color"
	"matrix"
	"piece"
)

func (board Board) IsChecked(c color.Color) bool {
	var king matrix.Point
	for i := 0; i < 8; i++ {
		for j := 0; j < 8; j++ {
			symbol := board.Matrix[i][j]
			if piece.King.IsSymbol(symbol) && color.WhichColor(symbol) == c {
				king = matrix.Point{i, j}
				break
			}
		}
	}
	enemy := c.Enemy()
	enemies := board.Matrix.Positions(enemy)
	for _, i := range enemies {
		symbol := board.Matrix[i.Y][i.X]
		piece := piece.WhichPiece(symbol)
		first := board.first[i.Y][i.X]
		diff := i.Diff(king)
		if piece.CanMove(diff, first, true) && board.Matrix.ExistBarrier(i, king) == false {
			return true
		}
	}
	return false
}

func (board Board) IsCheckMate(c color.Color) bool {
	if board.IsChecked(c) == false {
		return false
	}

	friend := board.Matrix.Positions(c)
	movable := append(board.Matrix.Positions(color.Empty), board.Matrix.Positions(c.Enemy())...)

	for _, i := range movable {
		for _, j := range friend {
			fsymbol := board.Matrix[j.Y][j.X]
			tsymbol := board.Matrix[i.Y][i.X]
			tcolor := color.WhichColor(tsymbol)
			if tcolor == c || tcolor == color.Unknown {
				continue
			}

			piece := piece.WhichPiece(fsymbol)
			first := board.first[j.Y][j.X]
			var diff matrix.Point
			if c == color.White {
				diff = j.Diff(i)
			} else if c == color.Black {
				diff = i.Diff(j)
			}
			toEnemy := color.WhichColor(tsymbol) == c.Enemy()
			if piece.CanMove(diff, first, toEnemy) && board.Matrix.ExistBarrier(j, i) == false {
				board.Matrix[i.Y][i.X] = fsymbol
				board.Matrix[j.Y][j.X] = ' '
				if board.IsChecked(c) == false {
					board.Matrix[i.Y][i.X] = tsymbol
					board.Matrix[j.Y][j.X] = fsymbol
					return false
				}
				board.Matrix[i.Y][i.X] = tsymbol
				board.Matrix[j.Y][j.X] = fsymbol
			}
		}
	}
	return true
}
<|endoftext|>