<commit_before>// Copyright 2019 Istio 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 pilot

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/onsi/gomega"

	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/echo"
	"istio.io/istio/pkg/test/framework/components/echo/echoboot"
	"istio.io/istio/pkg/test/framework/components/environment"
	"istio.io/istio/pkg/test/framework/components/galley"
	"istio.io/istio/pkg/test/framework/components/istioctl"
	"istio.io/istio/pkg/test/framework/components/namespace"
	"istio.io/istio/pkg/test/framework/components/pilot"
	"istio.io/istio/pkg/test/util/file"
)

const (
	describeSvcAOutput = `Service: a\..*
   Port: grpc 7070/GRPC targets pod port 7070
   Port: http 80/HTTP targets pod port 8090
7070 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
7070 Pod is .*, clients configured automatically
7070 VirtualService: a\..*
   when headers are end-user=jason
80 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
80 Pod is .*, clients configured automatically
80 VirtualService: a\..*
   when headers are end-user=jason
`

	describePodAOutput = `Pod: .*
   Pod Ports: 7070 \(app\), 8090 \(app\), 8080 \(app\), 3333 \(app\), 15090 \(istio-proxy\)
--------------------
Service: a\..*
   Port: grpc 7070\/GRPC targets pod port 7070
   Port: http 80\/HTTP targets pod port 8090
7070 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
7070 Pod is .*, clients configured automatically
7070 VirtualService: a\..*
   when headers are end-user=jason
80 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
80 Pod is .*, clients configured automatically
80 VirtualService: a\..*
   when headers are end-user=jason
`

	addToMeshPodAOutput = `deployment .* updated successfully with Istio sidecar injected.
Next Step: Add related labels to the deployment to align with Istio's requirement: https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
`
	removeFromMeshPodAOutput = `deployment .* updated successfully with Istio sidecar un-injected.`
)

// This test requires `--istio.test.env=kube` because it tests istioctl doing PodExec
// TestVersion does "istioctl version --remote=true" to verify the CLI understands the data plane version data
func TestVersion(t *testing.T) {
	framework.
		NewTest(t).
		RequiresEnvironment(environment.Kube).
		Run(func(ctx framework.TestContext) {
			g := galley.NewOrFail(t, ctx, galley.Config{})
			_ = pilot.NewOrFail(t, ctx, pilot.Config{Galley: g})
			cfg := i.Settings()

			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			args := []string{"version", "--remote=true", fmt.Sprintf("--istioNamespace=%s", cfg.SystemNamespace)}

			output := istioCtl.InvokeOrFail(t, args)

			// istioctl will return a single "control plane version" if all control plane versions match
			controlPlaneRegex := regexp.MustCompile(`control plane version: [a-z0-9\-]*`)
			if controlPlaneRegex.MatchString(output) {
				return
			}

			ctx.Logf("Did not find control plane version. This may mean components have different versions.")

			// At this point, we expect the version for each component
			expectedRegexps := []*regexp.Regexp{
				regexp.MustCompile(`citadel version: [a-z0-9\-]*`),
				regexp.MustCompile(`client version: [a-z0-9\-]*`),
				regexp.MustCompile(`egressgateway version: [a-z0-9\-]*`),
				regexp.MustCompile(`ingressgateway version: [a-z0-9\-]*`),
				regexp.MustCompile(`pilot version: [a-z0-9\-]*`),
				regexp.MustCompile(`galley version: [a-z0-9\-]*`),
				regexp.MustCompile(`policy version: [a-z0-9\-]*`),
				regexp.MustCompile(`sidecar-injector version: [a-z0-9\-]*`),
				regexp.MustCompile(`telemetry version: [a-z0-9\-]*`),
			}
			for _, regexp := range expectedRegexps {
				if !regexp.MatchString(output) {
					ctx.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v",
						strings.Join(args, " "), output, regexp)
				}
			}
		})
}

func TestDescribe(t *testing.T) {
	framework.NewTest(t).
		RequiresEnvironment(environment.Kube).
		RunParallel(func(ctx framework.TestContext) {
			ns := namespace.NewOrFail(ctx, ctx, namespace.Config{
				Prefix: "istioctl-describe",
				Inject: true,
			})

			deployment := file.AsStringOrFail(t, "../istioctl/testdata/a.yaml")
			g.ApplyConfigOrFail(t, ns, deployment)

			var a echo.Instance
			echoboot.NewBuilderOrFail(ctx, ctx).
				With(&a, echoConfig(ns, "a")).
				BuildOrFail(ctx)

			if err := a.WaitUntilCallable(a); err != nil {
				t.Fatal(err)
			}
			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			podID, err := getPodID(a)
			if err != nil {
				ctx.Fatalf("Could not get Pod ID: %v", err)
			}

			var output string
			var args []string
			g := gomega.NewGomegaWithT(t)

			// When this test passed the namespace through --namespace it was flakey
			// because istioctl uses a global variable for namespace, and this test may
			// run in parallel.
			args = []string{"--namespace=dummy",
				"x", "describe", "pod", fmt.Sprintf("%s.%s", podID, ns.Name())}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(describePodAOutput))

			args = []string{"--namespace=dummy",
				"x", "describe", "svc", fmt.Sprintf("a.%s", ns.Name())}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(describeSvcAOutput))
		})
}

func getPodID(i echo.Instance) (string, error) {
	wls, err := i.Workloads()
	if err != nil {
		return "", nil
	}

	for _, wl := range wls {
		hostname := strings.Split(wl.Sidecar().NodeID(), "~")[2]
		podID := strings.Split(hostname, ".")[0]
		return podID, nil
	}

	return "", fmt.Errorf("no workloads")
}

func TestAddToAndRemoveFromMesh(t *testing.T) {
	framework.NewTest(t).
		RequiresEnvironment(environment.Kube).
		RunParallel(func(ctx framework.TestContext) {
			ns := namespace.NewOrFail(t, ctx, namespace.Config{
				Prefix: "istioctl-add-to-mesh",
				Inject: true,
			})

			var a echo.Instance
			echoboot.NewBuilderOrFail(ctx, ctx).
				With(&a, echoConfig(ns, "a")).
				BuildOrFail(ctx)

			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			var output string
			var args []string
			g := gomega.NewGomegaWithT(t)

			// able to remove from mesh when the deployment is auto injected
			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"x", "remove-from-mesh", "service", "a"}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(removeFromMeshPodAOutput))

			// remove from mesh should be clean
			// users can add it back to mesh successfully
			if err := a.WaitUntilCallable(a); err != nil {
				t.Fatal(err)
			}

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"x", "add-to-mesh", "service", "a"}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(addToMeshPodAOutput))
		})
}

func TestProxyConfig(t *testing.T) {
	framework.NewTest(t).
		RequiresEnvironment(environment.Kube).
		RunParallel(func(ctx framework.TestContext) {
			ns := namespace.NewOrFail(ctx, ctx, namespace.Config{
				Prefix: "istioctl-pc",
				Inject: true,
			})

			var a echo.Instance
			echoboot.NewBuilderOrFail(ctx, ctx).
				With(&a, echoConfig(ns, "a")).
				BuildOrFail(ctx)

			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			podID, err := getPodID(a)
			if err != nil {
				ctx.Fatalf("Could not get Pod ID: %v", err)
			}

			var output string
			var args []string
			g := gomega.NewGomegaWithT(t)

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "bootstrap", podID}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput := jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.HaveKey("bootstrap"))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "cluster", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "endpoint", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "listener", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "route", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "secret", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.HaveKey("dynamicActiveSecrets"))
		})
}

func jsonUnmarshallOrFail(t *testing.T, context, s string) interface{} {
	t.Helper()
	var val interface{}

	// this is guarded by prettyPrint
	if err := json.Unmarshal([]byte(s), &val); err != nil {
		t.Fatalf("Could not unmarshal %s response %s", context, s)
	}
	return val
}
<commit_msg>Fix TestProxyConfig test flakes (#21603)<commit_after>// Copyright 2019 Istio 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 pilot

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/onsi/gomega"

	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/echo"
	"istio.io/istio/pkg/test/framework/components/echo/echoboot"
	"istio.io/istio/pkg/test/framework/components/environment"
	"istio.io/istio/pkg/test/framework/components/galley"
	"istio.io/istio/pkg/test/framework/components/istioctl"
	"istio.io/istio/pkg/test/framework/components/namespace"
	"istio.io/istio/pkg/test/framework/components/pilot"
	"istio.io/istio/pkg/test/util/file"
)

const (
	describeSvcAOutput = `Service: a\..*
   Port: grpc 7070/GRPC targets pod port 7070
   Port: http 80/HTTP targets pod port 8090
7070 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
7070 Pod is .*, clients configured automatically
7070 VirtualService: a\..*
   when headers are end-user=jason
80 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
80 Pod is .*, clients configured automatically
80 VirtualService: a\..*
   when headers are end-user=jason
`

	describePodAOutput = `Pod: .*
   Pod Ports: 7070 \(app\), 8090 \(app\), 8080 \(app\), 3333 \(app\), 15090 \(istio-proxy\)
--------------------
Service: a\..*
   Port: grpc 7070\/GRPC targets pod port 7070
   Port: http 80\/HTTP targets pod port 8090
7070 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
7070 Pod is .*, clients configured automatically
7070 VirtualService: a\..*
   when headers are end-user=jason
80 DestinationRule: a\..* for "a"
   Matching subsets: v1
   No Traffic Policy
80 Pod is .*, clients configured automatically
80 VirtualService: a\..*
   when headers are end-user=jason
`

	addToMeshPodAOutput = `deployment .* updated successfully with Istio sidecar injected.
Next Step: Add related labels to the deployment to align with Istio's requirement: https://istio.io/docs/setup/kubernetes/additional-setup/requirements/
`
	removeFromMeshPodAOutput = `deployment .* updated successfully with Istio sidecar un-injected.`
)

// This test requires `--istio.test.env=kube` because it tests istioctl doing PodExec
// TestVersion does "istioctl version --remote=true" to verify the CLI understands the data plane version data
func TestVersion(t *testing.T) {
	framework.
		NewTest(t).
		RequiresEnvironment(environment.Kube).
		Run(func(ctx framework.TestContext) {
			g := galley.NewOrFail(t, ctx, galley.Config{})
			_ = pilot.NewOrFail(t, ctx, pilot.Config{Galley: g})
			cfg := i.Settings()

			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			args := []string{"version", "--remote=true", fmt.Sprintf("--istioNamespace=%s", cfg.SystemNamespace)}

			output := istioCtl.InvokeOrFail(t, args)

			// istioctl will return a single "control plane version" if all control plane versions match
			controlPlaneRegex := regexp.MustCompile(`control plane version: [a-z0-9\-]*`)
			if controlPlaneRegex.MatchString(output) {
				return
			}

			ctx.Logf("Did not find control plane version. This may mean components have different versions.")

			// At this point, we expect the version for each component
			expectedRegexps := []*regexp.Regexp{
				regexp.MustCompile(`citadel version: [a-z0-9\-]*`),
				regexp.MustCompile(`client version: [a-z0-9\-]*`),
				regexp.MustCompile(`egressgateway version: [a-z0-9\-]*`),
				regexp.MustCompile(`ingressgateway version: [a-z0-9\-]*`),
				regexp.MustCompile(`pilot version: [a-z0-9\-]*`),
				regexp.MustCompile(`galley version: [a-z0-9\-]*`),
				regexp.MustCompile(`policy version: [a-z0-9\-]*`),
				regexp.MustCompile(`sidecar-injector version: [a-z0-9\-]*`),
				regexp.MustCompile(`telemetry version: [a-z0-9\-]*`),
			}
			for _, regexp := range expectedRegexps {
				if !regexp.MatchString(output) {
					ctx.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v",
						strings.Join(args, " "), output, regexp)
				}
			}
		})
}

func TestDescribe(t *testing.T) {
	framework.NewTest(t).
		RequiresEnvironment(environment.Kube).
		RunParallel(func(ctx framework.TestContext) {
			ns := namespace.NewOrFail(ctx, ctx, namespace.Config{
				Prefix: "istioctl-describe",
				Inject: true,
			})

			deployment := file.AsStringOrFail(t, "../istioctl/testdata/a.yaml")
			g.ApplyConfigOrFail(t, ns, deployment)

			var a echo.Instance
			echoboot.NewBuilderOrFail(ctx, ctx).
				With(&a, echoConfig(ns, "a")).
				BuildOrFail(ctx)

			if err := a.WaitUntilCallable(a); err != nil {
				t.Fatal(err)
			}
			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			podID, err := getPodID(a)
			if err != nil {
				ctx.Fatalf("Could not get Pod ID: %v", err)
			}

			var output string
			var args []string
			g := gomega.NewGomegaWithT(t)

			// When this test passed the namespace through --namespace it was flakey
			// because istioctl uses a global variable for namespace, and this test may
			// run in parallel.
			args = []string{"--namespace=dummy",
				"x", "describe", "pod", fmt.Sprintf("%s.%s", podID, ns.Name())}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(describePodAOutput))

			args = []string{"--namespace=dummy",
				"x", "describe", "svc", fmt.Sprintf("a.%s", ns.Name())}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(describeSvcAOutput))
		})
}

func getPodID(i echo.Instance) (string, error) {
	wls, err := i.Workloads()
	if err != nil {
		return "", nil
	}

	for _, wl := range wls {
		hostname := strings.Split(wl.Sidecar().NodeID(), "~")[2]
		podID := strings.Split(hostname, ".")[0]
		return podID, nil
	}

	return "", fmt.Errorf("no workloads")
}

func TestAddToAndRemoveFromMesh(t *testing.T) {
	framework.NewTest(t).
		RequiresEnvironment(environment.Kube).
		RunParallel(func(ctx framework.TestContext) {
			ns := namespace.NewOrFail(t, ctx, namespace.Config{
				Prefix: "istioctl-add-to-mesh",
				Inject: true,
			})

			var a echo.Instance
			echoboot.NewBuilderOrFail(ctx, ctx).
				With(&a, echoConfig(ns, "a")).
				BuildOrFail(ctx)

			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			var output string
			var args []string
			g := gomega.NewGomegaWithT(t)

			// able to remove from mesh when the deployment is auto injected
			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"x", "remove-from-mesh", "service", "a"}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(removeFromMeshPodAOutput))

			// remove from mesh should be clean
			// users can add it back to mesh successfully
			if err := a.WaitUntilCallable(a); err != nil {
				t.Fatal(err)
			}

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"x", "add-to-mesh", "service", "a"}
			output = istioCtl.InvokeOrFail(t, args)
			g.Expect(output).To(gomega.MatchRegexp(addToMeshPodAOutput))
		})
}

func TestProxyConfig(t *testing.T) {
	framework.NewTest(t).
		RequiresEnvironment(environment.Kube).
		Run(func(ctx framework.TestContext) {
			ns := namespace.NewOrFail(ctx, ctx, namespace.Config{
				Prefix: "istioctl-pc",
				Inject: true,
			})

			var a echo.Instance
			echoboot.NewBuilderOrFail(ctx, ctx).
				With(&a, echoConfig(ns, "a")).
				BuildOrFail(ctx)

			istioCtl := istioctl.NewOrFail(t, ctx, istioctl.Config{})

			podID, err := getPodID(a)
			if err != nil {
				ctx.Fatalf("Could not get Pod ID: %v", err)
			}

			var output string
			var args []string
			g := gomega.NewGomegaWithT(t)

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "bootstrap", podID}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput := jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.HaveKey("bootstrap"))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "cluster", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "endpoint", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "listener", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "route", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.Not(gomega.BeEmpty()))

			args = []string{fmt.Sprintf("--namespace=%s", ns.Name()),
				"pc", "secret", podID, "-o", "json"}
			output = istioCtl.InvokeOrFail(t, args)
			jsonOutput = jsonUnmarshallOrFail(t, strings.Join(args, " "), output)
			g.Expect(jsonOutput).To(gomega.HaveKey("dynamicActiveSecrets"))
		})
}

func jsonUnmarshallOrFail(t *testing.T, context, s string) interface{} {
	t.Helper()
	var val interface{}

	// this is guarded by prettyPrint
	if err := json.Unmarshal([]byte(s), &val); err != nil {
		t.Fatalf("Could not unmarshal %s response %s", context, s)
	}
	return val
}
<|endoftext|>


<commit_before>package iface

import (
	"log"
	"net"
	"os"
	//"strings"
	"syscall"
	"unsafe"
)

type adapterInfo struct {
	head *syscall.IpAdapterInfo
}

// From: https://code.google.com/p/go/source/browse/src/pkg/net/interface_windows.go
func bytePtrToString(p *uint8) string {
	a := (*[1000]uint8)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	return string(a[:i])
}

// From: https://code.google.com/p/go/source/browse/src/pkg/net/interface_windows.go
func getAdapterList() (*syscall.IpAdapterInfo, error) {
	b := make([]byte, 1000)
	l := uint32(len(b))
	a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
	// TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
	// contains IPv4 address list only. We should use another API
	// for fetching IPv6 stuff from the kernel.
	err := syscall.GetAdaptersInfo(a, &l)
	if err == syscall.ERROR_BUFFER_OVERFLOW {
		b = make([]byte, l)
		a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
		err = syscall.GetAdaptersInfo(a, &l)
	}
	if err != nil {
		return nil, os.NewSyscallError("GetAdaptersInfo", err)
	}
	return a, nil
}

func ipIsIPv4(ip net.IP) bool {
	p4 := ip.To4()
	return len(p4) == net.IPv4len
}

// getMask: get net.IPNet from net.IPAddr
func getMask(info *adapterInfo, index int, addr net.IPAddr) (net.IPNet, error) {

	ipNet := net.IPNet{}

	if info.head == nil {
		var err error
		info.head, err = getAdapterList()
		if err != nil {
			return ipNet, err
		}
	}

	v4 := ipIsIPv4(addr.IP)

	for ai := info.head; ai != nil; ai = ai.Next {
		if index == int(ai.Index) {
			for ipl := &ai.IpAddressList; ipl != nil; ipl = ipl.Next {
				// match
				//log.Printf("found: index=%v addr=[%s] mask=[%s]\n", index, ipl.IpAddress.String, ipl.IpMask.String)

				str := bytePtrToString(&ipl.IpMask.String[0])
				log.Printf("mask: [%v]\n", str)

				mask := net.ParseIP(str)
				if mask == nil {
					log.Printf("getMask UGH: mask: [%v]", mask)
					return ipNet, nil
				}

				ipNet.IP = addr.IP

				if v4 {
					m := mask.To4() // convert mask into 4-byte
					ipNet.Mask = net.IPv4Mask(m[0], m[1], m[2], m[3])
				} else {
					// IPv6 mask
					ipNet.Mask = net.IPMask{
						mask[0], mask[1], mask[2], mask[3],
						mask[4], mask[5], mask[6], mask[7],
						mask[8], mask[9], mask[10], mask[11],
						mask[12], mask[13], mask[14], mask[15],
					}
				}

				//log.Printf("ipNet: [%v]", ipNet)

				return ipNet, nil
			}
		}
	}

	return ipNet, nil
}

func GetInterfaceAddrs(i net.Interface) ([]net.Addr, error) {

	addrs, err := i.Addrs()
	if err != nil {
		return addrs, err
	}

	result := []net.Addr{}

	info := adapterInfo{}

	for _, a := range addrs {
		switch ad := a.(type) {
		case *net.IPNet:
			// linux, bsd, darwin, etc...
			result = append(result, a)
		case *net.IPAddr:
			// windows: missing netmask
			//log.Printf("GetInterfaceAddrs: net.IPAddr: %v: does not provide netmask", ad)
			ipNet, err := getMask(&info, i.Index, *ad)
			if err != nil {
				log.Printf("GetInterfaceAddrs: net.IPAddr: %v: error: %v", err)
				result = append(result, a)
				continue
			}
			result = append(result, &ipNet)
		default:
			// does this happen?
			log.Printf("GetInterfaceAddrs: unknown type: %v: does not provide netmask", ad)
			result = append(result, a)
		}
	}

	return result, nil
}
<commit_msg>Work-around for Golang lack of interface netmasks on Windows.<commit_after>package iface

import (
	"log"
	"net"
	"os"
	//"strings"
	"fmt"
	"syscall"
	"unsafe"
)

type adapterInfo struct {
	head *syscall.IpAdapterInfo
}

// From: https://code.google.com/p/go/source/browse/src/pkg/net/interface_windows.go
/*
func bytePtrToString(p *uint8) string {
	a := (*[1000]uint8)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	return string(a[:i])
}
*/

// From: https://code.google.com/p/go/source/browse/src/pkg/net/interface_windows.go
func getAdapterList() (*syscall.IpAdapterInfo, error) {
	b := make([]byte, 1000)
	l := uint32(len(b))
	a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
	// TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
	// contains IPv4 address list only. We should use another API
	// for fetching IPv6 stuff from the kernel.
	err := syscall.GetAdaptersInfo(a, &l)
	if err == syscall.ERROR_BUFFER_OVERFLOW {
		b = make([]byte, l)
		a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
		err = syscall.GetAdaptersInfo(a, &l)
	}
	if err != nil {
		return nil, os.NewSyscallError("GetAdaptersInfo", err)
	}
	return a, nil
}

func ipIsIPv4(ip net.IP) bool {
	p4 := ip.To4()
	return len(p4) == net.IPv4len
}

func toString(p []byte) string {
	for i, b := range p {
		if b == 0 {
			return string(p[:i])
		}
	}
	return string(p)
}

func parseIP(p [16]byte) net.IP {
	//str := bytePtrToString(&p[0])
	str := toString(p[:])
	//log.Printf("parseIP: [%v] len=%d", str, len(str))
	return net.ParseIP(str)
}

// getMask: get net.IPNet from net.IPAddr
func getMask(info *adapterInfo, index int, addr net.IPAddr) (net.IPNet, error) {

	ipNet := net.IPNet{}

	if info.head == nil {
		var err error
		info.head, err = getAdapterList()
		if err != nil {
			return ipNet, err
		}
	}

	v4 := ipIsIPv4(addr.IP)

	for ai := info.head; ai != nil; ai = ai.Next {
		if index == int(ai.Index) {
			for ipl := &ai.IpAddressList; ipl != nil; ipl = ipl.Next {

				ip := parseIP(ipl.IpAddress.String)
				if ip == nil {
					return ipNet, fmt.Errorf("getMask: parse error: [%v]", ip)
				}

				if !ip.Equal(addr.IP) {
					continue
				}

				// match
				//log.Printf("found: index=%v addr=[%s] mask=[%s]\n", index, ipl.IpAddress.String, ipl.IpMask.String)

				mask := parseIP(ipl.IpMask.String)
				if mask == nil {
					return ipNet, fmt.Errorf("getMask: parse error: [%v]", mask)
				}

				ipNet.IP = addr.IP

				if v4 {
					m := mask.To4() // convert mask into 4-byte
					ipNet.Mask = net.IPv4Mask(m[0], m[1], m[2], m[3])
				} else {
					// IPv6 mask
					ipNet.Mask = net.IPMask{
						mask[0], mask[1], mask[2], mask[3],
						mask[4], mask[5], mask[6], mask[7],
						mask[8], mask[9], mask[10], mask[11],
						mask[12], mask[13], mask[14], mask[15],
					}
				}

				//log.Printf("ipNet: [%v]", ipNet)

				return ipNet, nil
			}
		}
	}

	return ipNet, fmt.Errorf("getMask: not found: [%v]", addr)
}

/*
	GetInterfaceAddrs() is work-around for:
	http://code.google.com/p/go/issues/detail?id=5395
	Otherwise it could be replaced with net.Interface.Addrs()
*/
func GetInterfaceAddrs(i net.Interface) ([]net.Addr, error) {

	addrs, err := i.Addrs()
	if err != nil {
		return addrs, err
	}

	result := []net.Addr{}

	info := adapterInfo{}

	for _, a := range addrs {
		switch ad := a.(type) {
		case *net.IPNet:
			// linux, bsd, darwin, etc...
			result = append(result, a)
		case *net.IPAddr:
			// windows: missing netmask
			log.Printf("GetInterfaceAddrs: net.IPAddr: %v: does not provide netmask", ad)
			ipNet, err := getMask(&info, i.Index, *ad)
			if err != nil {
				log.Printf("GetInterfaceAddrs: net.IPAddr: %v: error: %v", err)
				result = append(result, a)
				continue
			}
			result = append(result, &ipNet)
		default:
			// does this happen?
			log.Printf("GetInterfaceAddrs: unknown type: %v: does not provide netmask", ad)
			result = append(result, a)
		}
	}

	return result, nil
}
<|endoftext|>


<commit_before>package rtsengine

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

/*
 World 2D grid. That is an array of acre structures.

*/

// World maintains the world state. This is the big one!
type World struct {
	Grid
}

// NewWorld will construct a random world of width and height specified.
// works on 'this'. Another way of thinking is width are the columns
// and height are the rows.
func NewWorld(width int, height int) *World {
	world := World{}

	// When the worldLocation is 0,0 then the grid IS the world.
	world.GenerateGrid(image.Point{0, 0}, width, height)

	return &world
}

// GenerateSimple will generate a simple world for basic testing.
// Good for testing pathing etcetera.
func (world *World) GenerateSimple() {

	// Make all the world grass!
	for i := range world.Matrix {
		for j := range world.Matrix[i] {
			world.Matrix[i][j].unit = nil
			world.Matrix[i][j].terrain = Grass
		}
	}

	// Randomly dot with trees and mountains
	s1 := rand.NewSource(time.Now().UnixNano())
	r1 := rand.New(s1)

	// Trees
	for i := 0; i < 10000; i++ {
		xr := r1.Intn(world.Span.Dx())
		yr := r1.Intn(world.Span.Dy())
		world.Matrix[xr][yr].terrain = Trees
	}

	// Mountains
	for i := 0; i < 10000; i++ {
		xr := r1.Intn(world.Span.Dx())
		yr := r1.Intn(world.Span.Dy())
		world.Matrix[xr][yr].terrain = Mountains
	}

	//world.Matrix[0][0].terrain = Trees
	//world.Matrix[0][1].terrain = Trees
	//world.Matrix[0][2].terrain = Trees
	//world.Matrix[0][3].terrain = Trees
	//world.Matrix[0][4].terrain = Trees

	centerPoint := world.Center()
	world.Matrix[centerPoint.X][centerPoint.Y].terrain = Grass

	// Generate a straight fence and go through anything.
	points := world.DirectLineBresenham(&image.Point{20, 20}, &image.Point{40, 40})

	for _, point := range points {
		world.Matrix[point.X][point.Y].terrain = Mountains
	}
}

// Center returns the x,y center of this View.
func (world *World) Center() image.Point {
	return image.Point{world.Span.Min.X + (world.Span.Dx() / 2), world.Span.Min.Y + (world.Span.Dy() / 2)}
}
<commit_msg>cleanup<commit_after>package rtsengine

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

/*
 World 2D grid. That is an array of acre structures.

*/

// World maintains the world state. This is the big one!
type World struct {
	Grid
}

// NewWorld will construct a random world of width and height specified.
// works on 'this'. Another way of thinking is width are the columns
// and height are the rows.
func NewWorld(width int, height int) *World {
	world := World{}

	// When the worldLocation is 0,0 then the grid IS the world.
	world.GenerateGrid(image.Point{0, 0}, width, height)

	return &world
}

// GenerateSimple will generate a simple world for basic testing.
// Good for testing pathing etcetera.
func (world *World) GenerateSimple() {

	// Make all the world grass!
	for i := range world.Matrix {
		for j := range world.Matrix[i] {
			world.Matrix[i][j].unit = nil
			world.Matrix[i][j].terrain = Grass
		}
	}

	// Randomly dot with trees and mountains
	s1 := rand.NewSource(time.Now().UnixNano())
	r1 := rand.New(s1)

	// Trees
	for i := 0; i < 10000; i++ {
		xr := r1.Intn(world.Span.Dx())
		yr := r1.Intn(world.Span.Dy())
		world.Matrix[xr][yr].terrain = Trees
	}

	// Mountains
	for i := 0; i < 10000; i++ {
		xr := r1.Intn(world.Span.Dx())
		yr := r1.Intn(world.Span.Dy())
		world.Matrix[xr][yr].terrain = Mountains
	}

	centerPoint := world.Center()
	world.Matrix[centerPoint.X][centerPoint.Y].terrain = Grass

	// Generate a straight fence and go through anything.
	points := world.DirectLineBresenham(&image.Point{20, 20}, &image.Point{40, 40})

	for _, point := range points {
		world.Matrix[point.X][point.Y].terrain = Mountains
	}
}

// Center returns the x,y center of this View.
func (world *World) Center() image.Point {
	return image.Point{world.Span.Min.X + (world.Span.Dx() / 2), world.Span.Min.Y + (world.Span.Dy() / 2)}
}
<|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 runtime

import "unsafe"

const (
	_Debugwbufs  = true    // if true check wbufs consistency
	_WorkbufSize = 1 * 256 // in bytes - if small wbufs are passed to GC in a timely fashion.
)

type workbufhdr struct {
	node  lfnode // must be first
	nobj  uintptr
	id    uintptr
	inuse bool       // This workbuf is in use by some gorotuine and is not on the work.empty/partial/full queues.
	log   [4]uintptr // line numbers forming a history of ownership changes to workbuf
}

type workbuf struct {
	workbufhdr
	// account for the above fields
	obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / ptrSize]uintptr
}

// workbuf factory routines. These funcs are used to manage the
// workbufs. They cache workbuf in the m struct field currentwbuf.
// If the GC asks for some work these are the only routines that
// make partially full wbufs available to the GC.
// Each of the gets and puts also take an distinct integer that is used
// to record a brief history of changes to ownership of the workbuf.
// The convention is to use a unique line number but any encoding
// is permissible. For example if you want to pass in 2 bits of information
// you could simple add lineno1*100000+lineno2.

// logget records the past few values of entry to aid in debugging.
// logget checks the buffer b is not currently in use.
func (b *workbuf) logget(entry uintptr) {
	if !_Debugwbufs {
		return
	}
	if b.inuse {
		println("runtime: logget fails log entry=", entry,
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("logget: get not legal")
	}
	b.inuse = true
	copy(b.log[1:], b.log[:])
	b.log[0] = entry
}

// logput records the past few values of entry to aid in debugging.
// logput checks the buffer b is currently in use.
func (b *workbuf) logput(entry uintptr) {
	if !_Debugwbufs {
		return
	}
	if !b.inuse {
		println("runtime:logput fails log entry=", entry,
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("logput: put not legal")
	}
	b.inuse = false
	copy(b.log[1:], b.log[:])
	b.log[0] = entry
}

func (b *workbuf) checknonempty() {
	if b.nobj == 0 {
		println("runtime: nonempty check fails",
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("workbuf is empty")
	}
}

func (b *workbuf) checkempty() {
	if b.nobj != 0 {
		println("runtime: empty check fails",
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("workbuf is not empty")
	}
}

// checknocurrentwbuf checks that the m's currentwbuf field is empty
func checknocurrentwbuf() {
	if getg().m.currentwbuf != 0 {
		throw("unexpected currentwbuf")
	}
}

// getempty pops an empty work buffer off the work.empty list,
// allocating new buffers if none are available.
// entry is used to record a brief history of ownership.
//go:nowritebarrier
func getempty(entry uintptr) *workbuf {
	var b *workbuf
	if work.empty != 0 {
		b = (*workbuf)(lfstackpop(&work.empty))
		if b != nil {
			b.checkempty()
		}
	}
	if b == nil {
		b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), _CacheLineSize, &memstats.gc_sys))
	}
	b.logget(entry)
	return b
}

// putempty puts a workbuf onto the work.empty list.
// Upon entry this go routine owns b. The lfstackpush relinquishes ownership.
//go:nowritebarrier
func putempty(b *workbuf, entry uintptr) {
	b.checkempty()
	b.logput(entry)
	lfstackpush(&work.empty, &b.node)
}

// putfull puts the workbuf on the work.full list for the GC.
// putfull accepts partially full buffers so the GC can avoid competing
// with the mutators for ownership of partially full buffers.
//go:nowritebarrier
func putfull(b *workbuf, entry uintptr) {
	b.checknonempty()
	b.logput(entry)
	lfstackpush(&work.full, &b.node)
}

// getpartialorempty tries to return a partially empty
// and if none are available returns an empty one.
// entry is used to provide a brief histoy of ownership
// using entry + xxx00000 to
// indicating that two line numbers in the call chain.
//go:nowritebarrier
func getpartialorempty(entry uintptr) *workbuf {
	var b *workbuf
	// If this m has a buf in currentwbuf then as an optimization
	// simply return that buffer. If it turns out currentwbuf
	// is full, put it on the work.full queue and get another
	// workbuf off the partial or empty queue.
	if getg().m.currentwbuf != 0 {
		b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0)))
		if b != nil {
			if b.nobj <= uintptr(len(b.obj)) {
				return b
			}
			putfull(b, entry+80100000)
		}
	}
	b = (*workbuf)(lfstackpop(&work.partial))
	if b != nil {
		b.logget(entry)
		return b
	}
	// Let getempty do the logget check but
	// use the entry to encode that it passed
	// through this routine.
	b = getempty(entry + 80700000)
	return b
}

// putpartial puts empty buffers on the work.empty queue,
// full buffers on the work.full queue and
// others on the work.partial queue.
// entry is used to provide a brief histoy of ownership
// using entry + xxx00000 to
// indicating that two call chain line numbers.
//go:nowritebarrier
func putpartial(b *workbuf, entry uintptr) {
	if b.nobj == 0 {
		putempty(b, entry+81500000)
	} else if b.nobj < uintptr(len(b.obj)) {
		b.logput(entry)
		lfstackpush(&work.partial, &b.node)
	} else if b.nobj == uintptr(len(b.obj)) {
		b.logput(entry)
		lfstackpush(&work.full, &b.node)
	} else {
		throw("putpartial: bad Workbuf b.nobj")
	}
}

// trygetfull tries to get a full or partially empty workbuffer.
// If one is not immediately available return nil
//go:nowritebarrier
func trygetfull(entry uintptr) *workbuf {
	b := (*workbuf)(lfstackpop(&work.full))
	if b == nil {
		b = (*workbuf)(lfstackpop(&work.partial))
	}
	if b != nil {
		b.logget(entry)
		b.checknonempty()
		return b
	}
	// full and partial are both empty so see if there
	// is an work available on currentwbuf.
	// This is an optimization to shift
	// processing from the STW marktermination phase into
	// the concurrent mark phase.
	if getg().m.currentwbuf != 0 {
		b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0)))
		if b != nil {
			if b.nobj != 0 {
				return b
			}
			putempty(b, 839)
			b = nil
		}
	}
	return b
}

// Get a full work buffer off the work.full or a partially
// filled one off the work.partial list. If nothing is available
// wait until all the other gc helpers have finished and then
// return nil.
// getfull acts as a barrier for work.nproc helpers. As long as one
// gchelper is actively marking objects it
// may create a workbuffer that the other helpers can work on.
// The for loop either exits when a work buffer is found
// or when _all_ of the work.nproc GC helpers are in the loop
// looking for work and thus not capable of creating new work.
// This is in fact the termination condition for the STW mark
// phase.
//go:nowritebarrier
func getfull(entry uintptr) *workbuf {
	b := (*workbuf)(lfstackpop(&work.full))
	if b != nil {
		b.logget(entry)
		b.checknonempty()
		return b
	}
	b = (*workbuf)(lfstackpop(&work.partial))
	if b != nil {
		b.logget(entry)
		return b
	}
	// Make sure that currentwbuf is also not a source for pointers to be
	// processed. This is an optimization that shifts processing
	// from the mark termination STW phase to the concurrent mark phase.
	if getg().m.currentwbuf != 0 {
		b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0)))
		if b != nil {
			if b.nobj != 0 {
				return b
			}
			putempty(b, 877)
			b = nil
		}
	}

	xadd(&work.nwait, +1)
	for i := 0; ; i++ {
		if work.full != 0 {
			xadd(&work.nwait, -1)
			b = (*workbuf)(lfstackpop(&work.full))
			if b == nil {
				b = (*workbuf)(lfstackpop(&work.partial))
			}
			if b != nil {
				b.logget(entry)
				b.checknonempty()
				return b
			}
			xadd(&work.nwait, +1)
		}
		if work.nwait == work.nproc {
			return nil
		}
		_g_ := getg()
		if i < 10 {
			_g_.m.gcstats.nprocyield++
			procyield(20)
		} else if i < 20 {
			_g_.m.gcstats.nosyield++
			osyield()
		} else {
			_g_.m.gcstats.nsleep++
			usleep(100)
		}
	}
}

//go:nowritebarrier
func handoff(b *workbuf) *workbuf {
	// Make new buffer with half of b's pointers.
	b1 := getempty(915)
	n := b.nobj / 2
	b.nobj -= n
	b1.nobj = n
	memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), n*unsafe.Sizeof(b1.obj[0]))
	_g_ := getg()
	_g_.m.gcstats.nhandoff++
	_g_.m.gcstats.nhandoffcnt += uint64(n)

	// Put b on full list - let first half of b get stolen.
	putfull(b, 942)
	return b1
}

// 1 when you are harvesting so that the write buffer code shade can
// detect calls during a presumable STW write barrier.
var harvestingwbufs uint32

// harvestwbufs moves non-empty workbufs to work.full from  m.currentwuf
// Must be in a STW phase.
// xchguintptr is used since there are write barrier calls from the GC helper
// routines even during a STW phase.
// TODO: chase down write barrier calls in STW phase and understand and eliminate
// them.
//go:nowritebarrier
func harvestwbufs() {
	// announce to write buffer that you are harvesting the currentwbufs
	atomicstore(&harvestingwbufs, 1)

	for mp := allm; mp != nil; mp = mp.alllink {
		wbuf := (*workbuf)(unsafe.Pointer(xchguintptr(&mp.currentwbuf, 0)))
		// TODO: beat write barriers out of the mark termination and eliminate xchg
		//		tempwbuf := (*workbuf)(unsafe.Pointer(tempm.currentwbuf))
		//		tempm.currentwbuf = 0
		if wbuf != nil {
			if wbuf.nobj == 0 {
				putempty(wbuf, 945)
			} else {
				putfull(wbuf, 947) //use full instead of partial so GC doesn't compete to get wbuf
			}
		}
	}

	atomicstore(&harvestingwbufs, 0)
}
<commit_msg>runtime: drop unused workbufhdr.id field<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 runtime

import "unsafe"

const (
	_Debugwbufs  = true    // if true check wbufs consistency
	_WorkbufSize = 1 * 256 // in bytes - if small wbufs are passed to GC in a timely fashion.
)

type workbufhdr struct {
	node  lfnode // must be first
	nobj  uintptr
	inuse bool       // This workbuf is in use by some gorotuine and is not on the work.empty/partial/full queues.
	log   [4]uintptr // line numbers forming a history of ownership changes to workbuf
}

type workbuf struct {
	workbufhdr
	// account for the above fields
	obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / ptrSize]uintptr
}

// workbuf factory routines. These funcs are used to manage the
// workbufs. They cache workbuf in the m struct field currentwbuf.
// If the GC asks for some work these are the only routines that
// make partially full wbufs available to the GC.
// Each of the gets and puts also take an distinct integer that is used
// to record a brief history of changes to ownership of the workbuf.
// The convention is to use a unique line number but any encoding
// is permissible. For example if you want to pass in 2 bits of information
// you could simple add lineno1*100000+lineno2.

// logget records the past few values of entry to aid in debugging.
// logget checks the buffer b is not currently in use.
func (b *workbuf) logget(entry uintptr) {
	if !_Debugwbufs {
		return
	}
	if b.inuse {
		println("runtime: logget fails log entry=", entry,
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("logget: get not legal")
	}
	b.inuse = true
	copy(b.log[1:], b.log[:])
	b.log[0] = entry
}

// logput records the past few values of entry to aid in debugging.
// logput checks the buffer b is currently in use.
func (b *workbuf) logput(entry uintptr) {
	if !_Debugwbufs {
		return
	}
	if !b.inuse {
		println("runtime:logput fails log entry=", entry,
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("logput: put not legal")
	}
	b.inuse = false
	copy(b.log[1:], b.log[:])
	b.log[0] = entry
}

func (b *workbuf) checknonempty() {
	if b.nobj == 0 {
		println("runtime: nonempty check fails",
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("workbuf is empty")
	}
}

func (b *workbuf) checkempty() {
	if b.nobj != 0 {
		println("runtime: empty check fails",
			"b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
			"b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
		throw("workbuf is not empty")
	}
}

// checknocurrentwbuf checks that the m's currentwbuf field is empty
func checknocurrentwbuf() {
	if getg().m.currentwbuf != 0 {
		throw("unexpected currentwbuf")
	}
}

// getempty pops an empty work buffer off the work.empty list,
// allocating new buffers if none are available.
// entry is used to record a brief history of ownership.
//go:nowritebarrier
func getempty(entry uintptr) *workbuf {
	var b *workbuf
	if work.empty != 0 {
		b = (*workbuf)(lfstackpop(&work.empty))
		if b != nil {
			b.checkempty()
		}
	}
	if b == nil {
		b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), _CacheLineSize, &memstats.gc_sys))
	}
	b.logget(entry)
	return b
}

// putempty puts a workbuf onto the work.empty list.
// Upon entry this go routine owns b. The lfstackpush relinquishes ownership.
//go:nowritebarrier
func putempty(b *workbuf, entry uintptr) {
	b.checkempty()
	b.logput(entry)
	lfstackpush(&work.empty, &b.node)
}

// putfull puts the workbuf on the work.full list for the GC.
// putfull accepts partially full buffers so the GC can avoid competing
// with the mutators for ownership of partially full buffers.
//go:nowritebarrier
func putfull(b *workbuf, entry uintptr) {
	b.checknonempty()
	b.logput(entry)
	lfstackpush(&work.full, &b.node)
}

// getpartialorempty tries to return a partially empty
// and if none are available returns an empty one.
// entry is used to provide a brief histoy of ownership
// using entry + xxx00000 to
// indicating that two line numbers in the call chain.
//go:nowritebarrier
func getpartialorempty(entry uintptr) *workbuf {
	var b *workbuf
	// If this m has a buf in currentwbuf then as an optimization
	// simply return that buffer. If it turns out currentwbuf
	// is full, put it on the work.full queue and get another
	// workbuf off the partial or empty queue.
	if getg().m.currentwbuf != 0 {
		b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0)))
		if b != nil {
			if b.nobj <= uintptr(len(b.obj)) {
				return b
			}
			putfull(b, entry+80100000)
		}
	}
	b = (*workbuf)(lfstackpop(&work.partial))
	if b != nil {
		b.logget(entry)
		return b
	}
	// Let getempty do the logget check but
	// use the entry to encode that it passed
	// through this routine.
	b = getempty(entry + 80700000)
	return b
}

// putpartial puts empty buffers on the work.empty queue,
// full buffers on the work.full queue and
// others on the work.partial queue.
// entry is used to provide a brief histoy of ownership
// using entry + xxx00000 to
// indicating that two call chain line numbers.
//go:nowritebarrier
func putpartial(b *workbuf, entry uintptr) {
	if b.nobj == 0 {
		putempty(b, entry+81500000)
	} else if b.nobj < uintptr(len(b.obj)) {
		b.logput(entry)
		lfstackpush(&work.partial, &b.node)
	} else if b.nobj == uintptr(len(b.obj)) {
		b.logput(entry)
		lfstackpush(&work.full, &b.node)
	} else {
		throw("putpartial: bad Workbuf b.nobj")
	}
}

// trygetfull tries to get a full or partially empty workbuffer.
// If one is not immediately available return nil
//go:nowritebarrier
func trygetfull(entry uintptr) *workbuf {
	b := (*workbuf)(lfstackpop(&work.full))
	if b == nil {
		b = (*workbuf)(lfstackpop(&work.partial))
	}
	if b != nil {
		b.logget(entry)
		b.checknonempty()
		return b
	}
	// full and partial are both empty so see if there
	// is an work available on currentwbuf.
	// This is an optimization to shift
	// processing from the STW marktermination phase into
	// the concurrent mark phase.
	if getg().m.currentwbuf != 0 {
		b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0)))
		if b != nil {
			if b.nobj != 0 {
				return b
			}
			putempty(b, 839)
			b = nil
		}
	}
	return b
}

// Get a full work buffer off the work.full or a partially
// filled one off the work.partial list. If nothing is available
// wait until all the other gc helpers have finished and then
// return nil.
// getfull acts as a barrier for work.nproc helpers. As long as one
// gchelper is actively marking objects it
// may create a workbuffer that the other helpers can work on.
// The for loop either exits when a work buffer is found
// or when _all_ of the work.nproc GC helpers are in the loop
// looking for work and thus not capable of creating new work.
// This is in fact the termination condition for the STW mark
// phase.
//go:nowritebarrier
func getfull(entry uintptr) *workbuf {
	b := (*workbuf)(lfstackpop(&work.full))
	if b != nil {
		b.logget(entry)
		b.checknonempty()
		return b
	}
	b = (*workbuf)(lfstackpop(&work.partial))
	if b != nil {
		b.logget(entry)
		return b
	}
	// Make sure that currentwbuf is also not a source for pointers to be
	// processed. This is an optimization that shifts processing
	// from the mark termination STW phase to the concurrent mark phase.
	if getg().m.currentwbuf != 0 {
		b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0)))
		if b != nil {
			if b.nobj != 0 {
				return b
			}
			putempty(b, 877)
			b = nil
		}
	}

	xadd(&work.nwait, +1)
	for i := 0; ; i++ {
		if work.full != 0 {
			xadd(&work.nwait, -1)
			b = (*workbuf)(lfstackpop(&work.full))
			if b == nil {
				b = (*workbuf)(lfstackpop(&work.partial))
			}
			if b != nil {
				b.logget(entry)
				b.checknonempty()
				return b
			}
			xadd(&work.nwait, +1)
		}
		if work.nwait == work.nproc {
			return nil
		}
		_g_ := getg()
		if i < 10 {
			_g_.m.gcstats.nprocyield++
			procyield(20)
		} else if i < 20 {
			_g_.m.gcstats.nosyield++
			osyield()
		} else {
			_g_.m.gcstats.nsleep++
			usleep(100)
		}
	}
}

//go:nowritebarrier
func handoff(b *workbuf) *workbuf {
	// Make new buffer with half of b's pointers.
	b1 := getempty(915)
	n := b.nobj / 2
	b.nobj -= n
	b1.nobj = n
	memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), n*unsafe.Sizeof(b1.obj[0]))
	_g_ := getg()
	_g_.m.gcstats.nhandoff++
	_g_.m.gcstats.nhandoffcnt += uint64(n)

	// Put b on full list - let first half of b get stolen.
	putfull(b, 942)
	return b1
}

// 1 when you are harvesting so that the write buffer code shade can
// detect calls during a presumable STW write barrier.
var harvestingwbufs uint32

// harvestwbufs moves non-empty workbufs to work.full from  m.currentwuf
// Must be in a STW phase.
// xchguintptr is used since there are write barrier calls from the GC helper
// routines even during a STW phase.
// TODO: chase down write barrier calls in STW phase and understand and eliminate
// them.
//go:nowritebarrier
func harvestwbufs() {
	// announce to write buffer that you are harvesting the currentwbufs
	atomicstore(&harvestingwbufs, 1)

	for mp := allm; mp != nil; mp = mp.alllink {
		wbuf := (*workbuf)(unsafe.Pointer(xchguintptr(&mp.currentwbuf, 0)))
		// TODO: beat write barriers out of the mark termination and eliminate xchg
		//		tempwbuf := (*workbuf)(unsafe.Pointer(tempm.currentwbuf))
		//		tempm.currentwbuf = 0
		if wbuf != nil {
			if wbuf.nobj == 0 {
				putempty(wbuf, 945)
			} else {
				putfull(wbuf, 947) //use full instead of partial so GC doesn't compete to get wbuf
			}
		}
	}

	atomicstore(&harvestingwbufs, 0)
}
<|endoftext|>


<commit_before>package scummatlas

import (
	_ "bufio"
	"fmt"
	goimage "image"
	"image/color"
	"io/ioutil"
	"log"
	"os"
	b "scummatlas/binaryutils"
	"scummatlas/image"
)

type BoxMatrix bool

type Room struct {
	data         []byte
	offset       int
	Width        int
	Height       int
	ObjCount     int
	TranspIndex  uint8
	Palette      color.Palette
	Image        *goimage.RGBA
	Boxes        []Box
	BoxMatrix    BoxMatrix
	ExitScript   Script
	EntryScript  Script
	LocalScripts map[int]Script
	Objects      map[int]Object
	//ColorCycle ColorCycle
}

func NewRoom(data []byte) *Room {
	blockName := room.getBlockName()
	if blockName != "ROOM" {
		panic("Can't find ROOM")
	}

	room := new(Room)
	room.data = data
	room.offset = 8
	room.Objects = make(map[int]Object)
	room.LocalScripts = make(map[int]Script)

	fmt.Printf("Block Name\tBlock Size\n=============\n")
	for room.offset < len(data) {
		blockName := room.getBlockName()
		fmt.Printf("%v\t%v bytes\n", blockName, room.getBlockSize())

		switch blockName {
		case "RMHD":
			room.parseRMHD()
		case "BOXD":
			room.parseBOXD()
		case "EXCD":
			//room.parseEXCD()
		case "ENCD":
			//room.parseENCD()
		case "EPAL":
			room.parseEPAL()
		case "CLUT":
			log.SetOutput(ioutil.Discard)
			room.parseCLUT()
		case "LSCR":
			//room.parseLSCR()
		case "OBCD":
			//room.parseOBCD()
		case "OBIM":
			room.parseOBIM()
		case "RMIM":
			room.parseRMIM()
		case "TRNS":
			room.parseTRNS()
		}
		log.SetOutput(os.Stdout)
		room.nextBlock()
	}

	return room
}

func (r *Room) parseLSCR() {
	scriptId := int(r.data[r.offset+8])
	scriptBlock := r.data[r.offset+9 : r.offset+r.getBlockSize()]
	script := parseScriptBlock(scriptBlock)
	r.LocalScripts[scriptId] = script
	if len(script) == 0 {
		fmt.Printf("DUMP from %x\n", r.offset+9)
		fmt.Printf("%x", scriptBlock)
	}
	fmt.Printf("\nLocal ScriptID 0x%02x, size %d, script %v\n", scriptId, r.getBlockSize(), script)
}

func (r *Room) parseTRNS() {
	r.TranspIndex = r.data[r.offset+8]
	fmt.Println("Transparent index", r.TranspIndex)
}

func (r *Room) parseOBIM() {
	blockSize := b.BE32(r.data, r.offset+4)
	objImg, id := NewObjectImageFromOBIM(r.data[r.offset:r.offset+blockSize], r)
	fmt.Printf("======================\nObject with id 0x%02X\n%+v\n", id, objImg)

	existing, ok := r.Objects[id]
	if !ok {
		existing = Object{Id: id}
	}
	existing.Image = objImg
	r.Objects[id] = existing
}

func (r *Room) parseOBCD() {
	blockSize := b.BE32(r.data, r.offset+4)
	object := NewObjectFromOBCD(r.data[r.offset : r.offset+blockSize])

	existingObject, ok := r.Objects[object.Id]
	if ok {
		object.Image = existingObject.Image
	}
	r.Objects[object.Id] = object
}

func (r *Room) parseENCD() {
	r.EntryScript = parseScriptBlock(r.data[r.offset+8 : r.offset+r.getBlockSize()])
}

func (r *Room) parseEPAL() {
	fmt.Println("EGA palette, not used")
	/*
		paletteData := r.data[r.offset+8 : r.offset+r.getBlockSize()]
		fmt.Println("Palette data size ", len(paletteData))

		r.Palette = image.ParsePalette(r.data[r.offset+8 : r.offset+8+3*256])
		fmt.Println("Palette length", len(r.Palette))
		fmt.Println(r.Palette)
	*/

}

func (r *Room) parseCLUT() {
	paletteData := r.data[r.offset+8 : r.offset+r.getBlockSize()]
	log.Println("Palette data size ", len(paletteData))

	r.Palette = image.ParsePalette(r.data[r.offset+8 : r.offset+8+3*256])
	log.Println("Palette length", len(r.Palette))

	for _, color := range r.Palette {
		r, g, b, _ := color.RGBA()
		r8, g8, b8 := uint8(r), uint8(g), uint8(b)
		log.Printf(" %x%x%x", r8, g8, b8)
	}
	log.Println()
}

func (r *Room) parseEXCD() {
	r.ExitScript = parseScriptBlock(r.data[r.offset+8 : r.offset+r.getBlockSize()])
}

func (r *Room) parseRMIM() {
	if string(r.data[r.offset+8:r.offset+12]) != "RMIH" {
		panic("Not room image header")
	}
	headerSize := b.BE32(r.data, r.offset+12)
	zBuffers := b.LE16(r.data, r.offset+16)
	fmt.Println("headerSize", headerSize)
	fmt.Println("zBuffers", zBuffers)

	if b.FourCharString(r.data, r.offset+18) != "IM00" {
		panic("Not room image found")
	}
	imageOffset := r.offset + 18
	imageSize := b.BE32(r.data, imageOffset+4)
	fmt.Println(b.FourCharString(r.data, imageOffset), imageSize)

	r.Image = image.ParseImage(
		r.data[imageOffset:imageOffset+4+imageSize],
		zBuffers,
		r.Width,
		r.Height,
		r.Palette,
		r.TranspIndex,
		false)
}

func (r *Room) parseBOXD() {
	boxCount := b.LE16(r.data, r.offset+8)
	var boxOffset int
	fmt.Println("BOXCOUNT", boxCount)
	for i := 0; i < boxCount; i++ {
		boxOffset = r.offset + 10 + i*20
		box := NewBox(r.data[boxOffset : boxOffset+20])
		r.Boxes = append(r.Boxes, box)
	}
}

func (r *Room) parseRMHD() {
	fmt.Println("RMHD offset", r.offset)
	r.Width = b.LE16(r.data, r.offset+8)
	r.Height = b.LE16(r.data, r.offset+10)
	r.ObjCount = b.LE16(r.data, r.offset+12)
	fmt.Printf("Room size %vx%v\n", r.Width, r.Height)
}

func (r Room) Print() {
	fmt.Println("Size: ", r.Width, r.Height)
	fmt.Println("Object count: ", r.ObjCount)
	fmt.Println("Boxes: ", len(r.Boxes))
}

func (r Room) getBlockName() string {
	return string(r.data[r.offset : r.offset+4])
}

func (r Room) getBlockSize() int {
	return b.BE32(r.data, r.offset+4)
}

func (r *Room) nextBlock() {
	r.offset += r.getBlockSize()
}

func NewBox(data []byte) Box {
	box := new(Box)

	box.ulx = b.LE16(data, 0)
	box.uly = b.LE16(data, 2)
	box.urx = b.LE16(data, 4)
	box.ury = b.LE16(data, 6)
	box.lrx = b.LE16(data, 8)
	box.lry = b.LE16(data, 10)
	box.llx = b.LE16(data, 12)
	box.lly = b.LE16(data, 14)
	box.mask = data[16]
	box.flags = data[17]
	box.scale = b.LE16(data, 18)

	return *box
}
<commit_msg>cleanup<commit_after>package scummatlas

import (
	_ "bufio"
	"fmt"
	goimage "image"
	"image/color"
	"io/ioutil"
	"log"
	"os"
	b "scummatlas/binaryutils"
	"scummatlas/image"
)

type BoxMatrix bool

type Room struct {
	data         []byte
	offset       int
	Width        int
	Height       int
	ObjCount     int
	TranspIndex  uint8
	Palette      color.Palette
	Image        *goimage.RGBA
	Boxes        []Box
	BoxMatrix    BoxMatrix
	ExitScript   Script
	EntryScript  Script
	LocalScripts map[int]Script
	Objects      map[int]Object
	//ColorCycle ColorCycle
}

func NewRoom(data []byte) *Room {
	room := new(Room)
	room.data = data
	room.offset = 0
	room.Objects = make(map[int]Object)
	room.LocalScripts = make(map[int]Script)

	blockName := room.getBlockName()
	if blockName != "ROOM" {
		panic("Can't find ROOM, found " + blockName + " instead")
	}

	room.offset = 8

	fmt.Printf("Block Name\tBlock Size\n=============\n")
	for room.offset < len(data) {
		blockName := room.getBlockName()
		fmt.Printf("%v\t%v bytes\n", blockName, room.getBlockSize())

		switch blockName {
		case "RMHD":
			room.parseRMHD()
		case "BOXD":
			room.parseBOXD()
		case "EXCD":
			//room.parseEXCD()
		case "ENCD":
			//room.parseENCD()
		case "EPAL":
			room.parseEPAL()
		case "CLUT":
			log.SetOutput(ioutil.Discard)
			room.parseCLUT()
		case "LSCR":
			//room.parseLSCR()
		case "OBCD":
			//room.parseOBCD()
		case "OBIM":
			room.parseOBIM()
		case "RMIM":
			room.parseRMIM()
		case "TRNS":
			room.parseTRNS()
		}
		log.SetOutput(os.Stdout)
		room.nextBlock()
	}

	return room
}

func (r *Room) parseLSCR() {
	scriptId := int(r.data[r.offset+8])
	scriptBlock := r.data[r.offset+9 : r.offset+r.getBlockSize()]
	script := parseScriptBlock(scriptBlock)
	r.LocalScripts[scriptId] = script
	if len(script) == 0 {
		fmt.Printf("DUMP from %x\n", r.offset+9)
		fmt.Printf("%x", scriptBlock)
	}
	fmt.Printf("\nLocal ScriptID 0x%02x, size %d, script %v\n", scriptId, r.getBlockSize(), script)
}

func (r *Room) parseTRNS() {
	r.TranspIndex = r.data[r.offset+8]
	fmt.Println("Transparent index", r.TranspIndex)
}

func (r *Room) parseOBIM() {
	blockSize := b.BE32(r.data, r.offset+4)
	objImg, id := NewObjectImageFromOBIM(r.data[r.offset:r.offset+blockSize], r)
	fmt.Printf("======================\nObject with id 0x%02X\n%+v\n", id, objImg)

	existing, ok := r.Objects[id]
	if !ok {
		existing = Object{Id: id}
	}
	existing.Image = objImg
	r.Objects[id] = existing
}

func (r *Room) parseOBCD() {
	blockSize := b.BE32(r.data, r.offset+4)
	object := NewObjectFromOBCD(r.data[r.offset : r.offset+blockSize])

	existingObject, ok := r.Objects[object.Id]
	if ok {
		object.Image = existingObject.Image
	}
	r.Objects[object.Id] = object
}

func (r *Room) parseENCD() {
	r.EntryScript = parseScriptBlock(r.data[r.offset+8 : r.offset+r.getBlockSize()])
}

func (r *Room) parseEPAL() {
	fmt.Println("EGA palette, not used")
	/*
		paletteData := r.data[r.offset+8 : r.offset+r.getBlockSize()]
		fmt.Println("Palette data size ", len(paletteData))

		r.Palette = image.ParsePalette(r.data[r.offset+8 : r.offset+8+3*256])
		fmt.Println("Palette length", len(r.Palette))
		fmt.Println(r.Palette)
	*/

}

func (r *Room) parseCLUT() {
	paletteData := r.data[r.offset+8 : r.offset+r.getBlockSize()]
	log.Println("Palette data size ", len(paletteData))

	r.Palette = image.ParsePalette(r.data[r.offset+8 : r.offset+8+3*256])
	log.Println("Palette length", len(r.Palette))

	for _, color := range r.Palette {
		r, g, b, _ := color.RGBA()
		r8, g8, b8 := uint8(r), uint8(g), uint8(b)
		log.Printf(" %x%x%x", r8, g8, b8)
	}
	log.Println()
}

func (r *Room) parseEXCD() {
	r.ExitScript = parseScriptBlock(r.data[r.offset+8 : r.offset+r.getBlockSize()])
}

func (r *Room) parseRMIM() {
	if string(r.data[r.offset+8:r.offset+12]) != "RMIH" {
		panic("Not room image header")
	}
	headerSize := b.BE32(r.data, r.offset+12)
	zBuffers := b.LE16(r.data, r.offset+16)
	fmt.Println("headerSize", headerSize)
	fmt.Println("zBuffers", zBuffers)

	if b.FourCharString(r.data, r.offset+18) != "IM00" {
		panic("Not room image found")
	}
	imageOffset := r.offset + 18
	imageSize := b.BE32(r.data, imageOffset+4)
	fmt.Println(b.FourCharString(r.data, imageOffset), imageSize)

	r.Image = image.ParseImage(
		r.data[imageOffset:imageOffset+4+imageSize],
		zBuffers,
		r.Width,
		r.Height,
		r.Palette,
		r.TranspIndex,
		false)
}

func (r *Room) parseBOXD() {
	boxCount := b.LE16(r.data, r.offset+8)
	var boxOffset int
	fmt.Println("BOXCOUNT", boxCount)
	for i := 0; i < boxCount; i++ {
		boxOffset = r.offset + 10 + i*20
		box := NewBox(r.data[boxOffset : boxOffset+20])
		r.Boxes = append(r.Boxes, box)
	}
}

func (r *Room) parseRMHD() {
	fmt.Println("RMHD offset", r.offset)
	r.Width = b.LE16(r.data, r.offset+8)
	r.Height = b.LE16(r.data, r.offset+10)
	r.ObjCount = b.LE16(r.data, r.offset+12)
	fmt.Printf("Room size %vx%v\n", r.Width, r.Height)
}

func (r Room) Print() {
	fmt.Println("Size: ", r.Width, r.Height)
	fmt.Println("Object count: ", r.ObjCount)
	fmt.Println("Boxes: ", len(r.Boxes))
}

func (r Room) getBlockName() string {
	return b.FourCharString(r.data, r.offset)
}

func (r Room) getBlockSize() int {
	return b.BE32(r.data, r.offset+4)
}

func (r *Room) nextBlock() {
	r.offset += r.getBlockSize()
}

func NewBox(data []byte) Box {
	box := new(Box)

	box.ulx = b.LE16(data, 0)
	box.uly = b.LE16(data, 2)
	box.urx = b.LE16(data, 4)
	box.ury = b.LE16(data, 6)
	box.lrx = b.LE16(data, 8)
	box.lry = b.LE16(data, 10)
	box.llx = b.LE16(data, 12)
	box.lly = b.LE16(data, 14)
	box.mask = data[16]
	box.flags = data[17]
	box.scale = b.LE16(data, 18)

	return *box
}
<|endoftext|>


<commit_before>package azurerm

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

	"github.com/Azure/azure-sdk-for-go/arm/keyvault"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
	"github.com/satori/uuid"
)

// As can be seen in the API definition, the Sku Family only supports the value
// `A` and is a required field
// https://github.com/Azure/azure-rest-api-specs/blob/master/arm-keyvault/2015-06-01/swagger/keyvault.json#L239
var armKeyVaultSkuFamily = "A"

func resourceArmKeyVault() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmKeyVaultCreate,
		Read:   resourceArmKeyVaultRead,
		Update: resourceArmKeyVaultCreate,
		Delete: resourceArmKeyVaultDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"sku": {
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								string(keyvault.Standard),
								string(keyvault.Premium),
							}, false),
						},
					},
				},
			},

			"vault_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tenant_id": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateUUID,
			},

			"access_policy": {
				Type:     schema.TypeList,
				Optional: true,
				MinItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"tenant_id": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateUUID,
						},
						"object_id": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateUUID,
						},
						"key_permissions": {
							Type:     schema.TypeList,
							Required: true,
							Elem: &schema.Schema{
								Type: schema.TypeString,
								ValidateFunc: validation.StringInSlice([]string{
									string(keyvault.KeyPermissionsAll),
									string(keyvault.KeyPermissionsBackup),
									string(keyvault.KeyPermissionsCreate),
									string(keyvault.KeyPermissionsDecrypt),
									string(keyvault.KeyPermissionsDelete),
									string(keyvault.KeyPermissionsEncrypt),
									string(keyvault.KeyPermissionsGet),
									string(keyvault.KeyPermissionsImport),
									string(keyvault.KeyPermissionsList),
									string(keyvault.KeyPermissionsRestore),
									string(keyvault.KeyPermissionsSign),
									string(keyvault.KeyPermissionsUnwrapKey),
									string(keyvault.KeyPermissionsUpdate),
									string(keyvault.KeyPermissionsVerify),
									string(keyvault.KeyPermissionsWrapKey),
								}, false),
							},
						},
						"secret_permissions": {
							Type:     schema.TypeList,
							Required: true,
							Elem: &schema.Schema{
								Type: schema.TypeString,
								ValidateFunc: validation.StringInSlice([]string{
									string(keyvault.SecretPermissionsAll),
									string(keyvault.SecretPermissionsDelete),
									string(keyvault.SecretPermissionsGet),
									string(keyvault.SecretPermissionsList),
									string(keyvault.SecretPermissionsSet),
								}, false),
							},
						},
					},
				},
			},

			"enabled_for_deployment": {
				Type:     schema.TypeBool,
				Optional: true,
			},

			"enabled_for_disk_encryption": {
				Type:     schema.TypeBool,
				Optional: true,
			},

			"enabled_for_template_deployment": {
				Type:     schema.TypeBool,
				Optional: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmKeyVaultCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).keyVaultClient
	log.Printf("[INFO] preparing arguments for Azure ARM KeyVault creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tenantUUID := uuid.FromStringOrNil(d.Get("tenant_id").(string))
	enabledForDeployment := d.Get("enabled_for_deployment").(bool)
	enabledForDiskEncryption := d.Get("enabled_for_disk_encryption").(bool)
	enabledForTemplateDeployment := d.Get("enabled_for_template_deployment").(bool)
	tags := d.Get("tags").(map[string]interface{})

	parameters := keyvault.VaultCreateOrUpdateParameters{
		Location: &location,
		Properties: &keyvault.VaultProperties{
			TenantID:                     &tenantUUID,
			Sku:                          expandKeyVaultSku(d),
			AccessPolicies:               expandKeyVaultAccessPolicies(d),
			EnabledForDeployment:         &enabledForDeployment,
			EnabledForDiskEncryption:     &enabledForDiskEncryption,
			EnabledForTemplateDeployment: &enabledForTemplateDeployment,
		},
		Tags: expandTags(tags),
	}

	_, err := client.CreateOrUpdate(resGroup, name, parameters)
	if err != nil {
		return err
	}

	read, err := client.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read KeyVault %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmKeyVaultRead(d, meta)
}

func resourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).keyVaultClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["vaults"]

	resp, err := client.Get(resGroup, name)
	if err != nil {
		return fmt.Errorf("Error making Read request on Azure KeyVault %s: %s", name, err)
	}
	if resp.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	}

	d.Set("name", resp.Name)
	d.Set("resource_group_name", resGroup)
	d.Set("location", azureRMNormalizeLocation(*resp.Location))
	d.Set("tenant_id", resp.Properties.TenantID.String())
	d.Set("enabled_for_deployment", resp.Properties.EnabledForDeployment)
	d.Set("enabled_for_disk_encryption", resp.Properties.EnabledForDiskEncryption)
	d.Set("enabled_for_template_deployment", resp.Properties.EnabledForTemplateDeployment)
	d.Set("sku", flattenKeyVaultSku(resp.Properties.Sku))
	d.Set("access_policy", flattenKeyVaultAccessPolicies(resp.Properties.AccessPolicies))
	d.Set("vault_uri", resp.Properties.VaultURI)

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmKeyVaultDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).keyVaultClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["vaults"]

	_, err = client.Delete(resGroup, name)

	return err
}

func expandKeyVaultSku(d *schema.ResourceData) *keyvault.Sku {
	skuSets := d.Get("sku").(*schema.Set).List()
	sku := skuSets[0].(map[string]interface{})

	return &keyvault.Sku{
		Family: &armKeyVaultSkuFamily,
		Name:   keyvault.SkuName(sku["name"].(string)),
	}
}

func expandKeyVaultAccessPolicies(d *schema.ResourceData) *[]keyvault.AccessPolicyEntry {
	policies := d.Get("access_policy").([]interface{})
	result := make([]keyvault.AccessPolicyEntry, 0, len(policies))

	for _, policySet := range policies {
		policyRaw := policySet.(map[string]interface{})

		keyPermissionsRaw := policyRaw["key_permissions"].([]interface{})
		keyPermissions := []keyvault.KeyPermissions{}
		for _, permission := range keyPermissionsRaw {
			keyPermissions = append(keyPermissions, keyvault.KeyPermissions(permission.(string)))
		}

		secretPermissionsRaw := policyRaw["secret_permissions"].([]interface{})
		secretPermissions := []keyvault.SecretPermissions{}
		for _, permission := range secretPermissionsRaw {
			secretPermissions = append(secretPermissions, keyvault.SecretPermissions(permission.(string)))
		}

		policy := keyvault.AccessPolicyEntry{
			Permissions: &keyvault.Permissions{
				Keys:    &keyPermissions,
				Secrets: &secretPermissions,
			},
		}

		tenantUUID := uuid.FromStringOrNil(policyRaw["tenant_id"].(string))
		policy.TenantID = &tenantUUID
		objectUUID := policyRaw["object_id"].(string)
		policy.ObjectID = &objectUUID

		result = append(result, policy)
	}

	return &result
}

func flattenKeyVaultSku(sku *keyvault.Sku) []interface{} {
	result := map[string]interface{}{
		"name": string(sku.Name),
	}

	return []interface{}{result}
}

func flattenKeyVaultAccessPolicies(policies *[]keyvault.AccessPolicyEntry) []interface{} {
	result := make([]interface{}, 0, len(*policies))

	for _, policy := range *policies {
		policyRaw := make(map[string]interface{})

		keyPermissionsRaw := make([]interface{}, 0, len(*policy.Permissions.Keys))
		for _, keyPermission := range *policy.Permissions.Keys {
			keyPermissionsRaw = append(keyPermissionsRaw, string(keyPermission))
		}

		secretPermissionsRaw := make([]interface{}, 0, len(*policy.Permissions.Secrets))
		for _, secretPermission := range *policy.Permissions.Secrets {
			secretPermissionsRaw = append(secretPermissionsRaw, string(secretPermission))
		}

		policyRaw["tenant_id"] = policy.TenantID.String()
		policyRaw["object_id"] = policy.ObjectID
		policyRaw["key_permissions"] = keyPermissionsRaw
		policyRaw["secret_permissions"] = secretPermissionsRaw

		result = append(result, policyRaw)
	}

	return result
}
<commit_msg>Add `MaxItems` to `access_policy`<commit_after>package azurerm

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

	"github.com/Azure/azure-sdk-for-go/arm/keyvault"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
	"github.com/satori/uuid"
)

// As can be seen in the API definition, the Sku Family only supports the value
// `A` and is a required field
// https://github.com/Azure/azure-rest-api-specs/blob/master/arm-keyvault/2015-06-01/swagger/keyvault.json#L239
var armKeyVaultSkuFamily = "A"

func resourceArmKeyVault() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmKeyVaultCreate,
		Read:   resourceArmKeyVaultRead,
		Update: resourceArmKeyVaultCreate,
		Delete: resourceArmKeyVaultDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"sku": {
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								string(keyvault.Standard),
								string(keyvault.Premium),
							}, false),
						},
					},
				},
			},

			"vault_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tenant_id": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateUUID,
			},

			"access_policy": {
				Type:     schema.TypeList,
				Optional: true,
				MinItems: 1,
				MaxItems: 16,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"tenant_id": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateUUID,
						},
						"object_id": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateUUID,
						},
						"key_permissions": {
							Type:     schema.TypeList,
							Required: true,
							Elem: &schema.Schema{
								Type: schema.TypeString,
								ValidateFunc: validation.StringInSlice([]string{
									string(keyvault.KeyPermissionsAll),
									string(keyvault.KeyPermissionsBackup),
									string(keyvault.KeyPermissionsCreate),
									string(keyvault.KeyPermissionsDecrypt),
									string(keyvault.KeyPermissionsDelete),
									string(keyvault.KeyPermissionsEncrypt),
									string(keyvault.KeyPermissionsGet),
									string(keyvault.KeyPermissionsImport),
									string(keyvault.KeyPermissionsList),
									string(keyvault.KeyPermissionsRestore),
									string(keyvault.KeyPermissionsSign),
									string(keyvault.KeyPermissionsUnwrapKey),
									string(keyvault.KeyPermissionsUpdate),
									string(keyvault.KeyPermissionsVerify),
									string(keyvault.KeyPermissionsWrapKey),
								}, false),
							},
						},
						"secret_permissions": {
							Type:     schema.TypeList,
							Required: true,
							Elem: &schema.Schema{
								Type: schema.TypeString,
								ValidateFunc: validation.StringInSlice([]string{
									string(keyvault.SecretPermissionsAll),
									string(keyvault.SecretPermissionsDelete),
									string(keyvault.SecretPermissionsGet),
									string(keyvault.SecretPermissionsList),
									string(keyvault.SecretPermissionsSet),
								}, false),
							},
						},
					},
				},
			},

			"enabled_for_deployment": {
				Type:     schema.TypeBool,
				Optional: true,
			},

			"enabled_for_disk_encryption": {
				Type:     schema.TypeBool,
				Optional: true,
			},

			"enabled_for_template_deployment": {
				Type:     schema.TypeBool,
				Optional: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmKeyVaultCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).keyVaultClient
	log.Printf("[INFO] preparing arguments for Azure ARM KeyVault creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tenantUUID := uuid.FromStringOrNil(d.Get("tenant_id").(string))
	enabledForDeployment := d.Get("enabled_for_deployment").(bool)
	enabledForDiskEncryption := d.Get("enabled_for_disk_encryption").(bool)
	enabledForTemplateDeployment := d.Get("enabled_for_template_deployment").(bool)
	tags := d.Get("tags").(map[string]interface{})

	parameters := keyvault.VaultCreateOrUpdateParameters{
		Location: &location,
		Properties: &keyvault.VaultProperties{
			TenantID:                     &tenantUUID,
			Sku:                          expandKeyVaultSku(d),
			AccessPolicies:               expandKeyVaultAccessPolicies(d),
			EnabledForDeployment:         &enabledForDeployment,
			EnabledForDiskEncryption:     &enabledForDiskEncryption,
			EnabledForTemplateDeployment: &enabledForTemplateDeployment,
		},
		Tags: expandTags(tags),
	}

	_, err := client.CreateOrUpdate(resGroup, name, parameters)
	if err != nil {
		return err
	}

	read, err := client.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read KeyVault %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmKeyVaultRead(d, meta)
}

func resourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).keyVaultClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["vaults"]

	resp, err := client.Get(resGroup, name)
	if err != nil {
		return fmt.Errorf("Error making Read request on Azure KeyVault %s: %s", name, err)
	}
	if resp.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	}

	d.Set("name", resp.Name)
	d.Set("resource_group_name", resGroup)
	d.Set("location", azureRMNormalizeLocation(*resp.Location))
	d.Set("tenant_id", resp.Properties.TenantID.String())
	d.Set("enabled_for_deployment", resp.Properties.EnabledForDeployment)
	d.Set("enabled_for_disk_encryption", resp.Properties.EnabledForDiskEncryption)
	d.Set("enabled_for_template_deployment", resp.Properties.EnabledForTemplateDeployment)
	d.Set("sku", flattenKeyVaultSku(resp.Properties.Sku))
	d.Set("access_policy", flattenKeyVaultAccessPolicies(resp.Properties.AccessPolicies))
	d.Set("vault_uri", resp.Properties.VaultURI)

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmKeyVaultDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).keyVaultClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["vaults"]

	_, err = client.Delete(resGroup, name)

	return err
}

func expandKeyVaultSku(d *schema.ResourceData) *keyvault.Sku {
	skuSets := d.Get("sku").(*schema.Set).List()
	sku := skuSets[0].(map[string]interface{})

	return &keyvault.Sku{
		Family: &armKeyVaultSkuFamily,
		Name:   keyvault.SkuName(sku["name"].(string)),
	}
}

func expandKeyVaultAccessPolicies(d *schema.ResourceData) *[]keyvault.AccessPolicyEntry {
	policies := d.Get("access_policy").([]interface{})
	result := make([]keyvault.AccessPolicyEntry, 0, len(policies))

	for _, policySet := range policies {
		policyRaw := policySet.(map[string]interface{})

		keyPermissionsRaw := policyRaw["key_permissions"].([]interface{})
		keyPermissions := []keyvault.KeyPermissions{}
		for _, permission := range keyPermissionsRaw {
			keyPermissions = append(keyPermissions, keyvault.KeyPermissions(permission.(string)))
		}

		secretPermissionsRaw := policyRaw["secret_permissions"].([]interface{})
		secretPermissions := []keyvault.SecretPermissions{}
		for _, permission := range secretPermissionsRaw {
			secretPermissions = append(secretPermissions, keyvault.SecretPermissions(permission.(string)))
		}

		policy := keyvault.AccessPolicyEntry{
			Permissions: &keyvault.Permissions{
				Keys:    &keyPermissions,
				Secrets: &secretPermissions,
			},
		}

		tenantUUID := uuid.FromStringOrNil(policyRaw["tenant_id"].(string))
		policy.TenantID = &tenantUUID
		objectUUID := policyRaw["object_id"].(string)
		policy.ObjectID = &objectUUID

		result = append(result, policy)
	}

	return &result
}

func flattenKeyVaultSku(sku *keyvault.Sku) []interface{} {
	result := map[string]interface{}{
		"name": string(sku.Name),
	}

	return []interface{}{result}
}

func flattenKeyVaultAccessPolicies(policies *[]keyvault.AccessPolicyEntry) []interface{} {
	result := make([]interface{}, 0, len(*policies))

	for _, policy := range *policies {
		policyRaw := make(map[string]interface{})

		keyPermissionsRaw := make([]interface{}, 0, len(*policy.Permissions.Keys))
		for _, keyPermission := range *policy.Permissions.Keys {
			keyPermissionsRaw = append(keyPermissionsRaw, string(keyPermission))
		}

		secretPermissionsRaw := make([]interface{}, 0, len(*policy.Permissions.Secrets))
		for _, secretPermission := range *policy.Permissions.Secrets {
			secretPermissionsRaw = append(secretPermissionsRaw, string(secretPermission))
		}

		policyRaw["tenant_id"] = policy.TenantID.String()
		policyRaw["object_id"] = policy.ObjectID
		policyRaw["key_permissions"] = keyPermissionsRaw
		policyRaw["secret_permissions"] = secretPermissionsRaw

		result = append(result, policyRaw)
	}

	return result
}
<|endoftext|>


<commit_before>package turbowookie

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"
	"time"
)

// TWHandler is our custom http.Handler used to actually do the HTTP stuff.
type TWHandler struct {
	// MpdClient is our MPD Client, used to tell MPD to do things. Important
	// things.
	MpdClient *TWMPDClient

	// ServerConfig is a map of configuration key/values found in
	// a config.yaml file.
	ServerConfig map[string]string

	// Router is a mux.Router, it's what really does all the HTTP stuff, we just
	// act as the interface. And the HandlerFuncs
	Router *mux.Router

	// updater is a channel used by our long poller/polar system. It contains
	// the message of what's been changed.
	updater chan string

	// pollerClients is the number of people currently connected to the long
	// poller.
	pollerClients int
}

// NewTWHandler creates a new TWHandler, using the passed in filename as a
// yaml file containing the server's configuation settings.
func NewTWHandler(filename string, serveDart, startMPD bool, portOverride int) (*TWHandler, error) {
	// make us a pointer to a handler.
	h := &TWHandler{}

	// attempt to read the passed in config file. See `yaml.go` for more info.
	config, err := ReadConfig(filename)
	if err != nil {
		return nil, err
	}

	if !(config["server_port"] != "9000" && portOverride == 9000) {
		config["server_port"] = strconv.Itoa(portOverride)
	}

	h.ServerConfig = config
	h.MpdClient = NewTWMPDClient(h.ServerConfig, startMPD) // see TWMPDClient.go

	// Make sure there's a server to connect to, and run some other startup
	// commands (like making sure there's music playing...).
	err = h.MpdClient.Startup()
	if err != nil {
		log.Fatal("Error running the TWMPDClient startup...\n", err)
	}

	// Actually make our HTTP Router
	h.Router = mux.NewRouter()

	// Let us play the MPD without having to deal with cross origin stuff.
	// Because cross origin is kinda a bitch.
	h.Router.HandleFunc("/stream", httputil.NewSingleHostReverseProxy(
		&url.URL{
			Scheme: "http",
			Host:   h.ServerConfig["mpd_domain"] + ":" + h.ServerConfig["mpd_http_port"],
			Path:   "/",
		}).ServeHTTP)

	h.Router.HandleFunc("/songs", h.listSongs)
	h.Router.HandleFunc("/artists", h.listArtists)
	h.Router.HandleFunc("/albums", h.listArtistAlbums)
	h.Router.HandleFunc("/current", h.getCurrentSong)
	h.Router.HandleFunc("/upcoming", h.getUpcomingSongs)
	h.Router.HandleFunc("/add", h.addSong)
	h.Router.HandleFunc("/polar", h.bear)

	// This needs to be last, otherwise it'll override all routes after it
	// because we're matching EVERYTHING.
	fileDir := h.ServerConfig["turbo_wookie_directory"] + "/frontend/turbo_wookie"
	if serveDart {
		fileDir += "/web"
	} else {
		fileDir += "/build"
	}
	h.Router.PathPrefix("/").Handler(http.FileServer(http.Dir(fileDir)))

	// setup our poller/polar stuff.
	h.updater = make(chan string)
	h.pollerClients = 0

	// nothing bad happened. Suprise!
	return h, nil
}

// Make TWHandler an HTTP.Handler. Hackily. Just pass up the underlying
// Router's function.
func (h *TWHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Router.ServeHTTP(w, r)
}

// HandleFunc make TWHandler extendible...
// Same as ServeHTTP, just pass up the Router's function.
func (h *TWHandler) HandleFunc(path string, f func(w http.ResponseWriter, r *http.Request)) *mux.Route {
	return h.Router.HandleFunc(path, f)
}

// ListenAndServe serve up some TurboWookie. And setup an MPD Watcher to see
// when things happen to the stream. Because things sometimes happen to the
// stream.
func (h *TWHandler) ListenAndServe() error {
	// Setup a watcher.
	WatchMPD(h.ServerConfig["mpd_domain"]+":"+h.ServerConfig["mpd_control_port"], h)

	port := ":" + h.ServerConfig["server_port"]
	log.Println("Starting server on " + port)
	return http.ListenAndServe(port, h)
}

/************************
    HANDLER FUNCTIONS
************************/

// List all songs in the library, and information about those songs.
func (h *TWHandler) listSongs(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	artist, ok1 := r.Form["artist"]
	album, ok2 := r.Form["album"]

	var songs []map[string]string
	var err error

	log.Println("oks")
	log.Println(ok1)
	log.Println(ok2)

	if ok1 && ok2 {
		songs, err = h.MpdClient.GetSongs(artist[0], album[0])
	} else if ok1 && !ok2 {
		songs, err = h.MpdClient.GetSongs(artist[0], "")
	} else {
		songs, err = h.MpdClient.GetFiles()
	}

	if err != nil {
		printError(w, "An error occured while processing your request", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(songs))
}

func (h *TWHandler) listArtists(w http.ResponseWriter, r *http.Request) {
	artists, err := h.MpdClient.GetArtists()
	if err != nil {
		printError(w, "An error occured while processing your request", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(artists))
}

func (h *TWHandler) listArtistAlbums(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	artist, ok := r.Form["artist"]
	var albums []string
	var err error
	if !ok {
		albums, err = h.MpdClient.GetAlbums("")
	} else {
		albums, err = h.MpdClient.GetAlbums(artist[0])
	}
	if err != nil {
		printError(w, "An error occured while processing your request", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(albums))
}

// Return information about the currently playing song.
func (h *TWHandler) getCurrentSong(w http.ResponseWriter, r *http.Request) {
	currentSong, err := h.MpdClient.CurrentSong()
	if err != nil {
		printError(w, "Couldn't get current song info", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(currentSong))
}

// Return a list of all upcoming songs in the playlist.
// As in, return `playlist[current song + 1 :]`.
func (h *TWHandler) getUpcomingSongs(w http.ResponseWriter, r *http.Request) {
	upcoming, err := h.MpdClient.GetUpcoming()
	if err != nil {
		printError(w, "Couldn't get upcoming playlist", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(upcoming))
}

// Add a song to the playlist. Using the format
//    `/add?song=[FilePath of song]`
func (h *TWHandler) addSong(w http.ResponseWriter, r *http.Request) {
	// for some reason Go doesn't do this automatically.
	r.ParseForm()

	// Get the song from the GET request variables,
	// and check that there's actually something called `song` in the request.
	song, ok := r.Form["song"]
	if !ok {
		printError(w, "No song specified", nil)
		return
	}

	// Attempt to add the song to the playlist
	err := h.MpdClient.Add(song[0])
	if err != nil {
		printError(w, "Unknown song", err)
		return
	}

	// Return a simple note saying that we got the song
	m := make(map[string]string)
	m["note"] = "Added song: " + song[0]
	fmt.Fprintf(w, jsoniffy(m))

	// tell long pollers that the playlist changed.
	h.PolarChanged("playlist")
}

// Our long poller. Accessed through `/polar`.
// Clients connect to this, and wait for either five minutes (after which
// they probably reconnect) or until the server tells them something has
// changed.
//
// This is done so that clients don't need to make periodic requests
// asking for the current playlist.
func (h *TWHandler) bear(w http.ResponseWriter, r *http.Request) {
	// we got another live one.
	h.pollerClients += 1

	// Setup a timeout to make sure the client doesn't sit here forever.
	timeout := make(chan bool)
	defer func() { h.pollerClients -= 1 }()
	go func() {
		// if after five minutes nothing has changed, timeout and have the client
		// connect again.
		time.Sleep(5 * time.Minute)
		timeout <- true
	}()

	// Either the updater has news or the timeout expired.
	// Depending on which, tell the client something or nothing changed.
	select {
	case msg := <-h.updater:
		fmt.Fprintf(w, msg)
		if h.pollerClients > 1 {
			h.updater <- msg
		}
	case <-timeout:
		m := make(map[string]string)
		m["changed"] = "nothing"

		fmt.Fprintf(w, jsoniffy(m))
	}
}

/************************
    HELPER FUNCTIONS
************************/

// Print an error the the screen, and send a simple message to the client.
func printError(w http.ResponseWriter, msg string, err error) {
	log.Println("ERROR:", err)
	log.Println("Sending to client:", msg)
	fmt.Fprintf(w, msg+"\n")
}

// Turn things into JSON.
func jsoniffy(v interface{}) string {
	obj, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		log.Print("Couldn't turn something into JSON: ", v)
		log.Fatal(err)
	}

	return string(obj)
}

// PolarChanged tell clients connected to our long-poll system that something
// (element) has changed.
func (h *TWHandler) PolarChanged(element string) {
	if h.pollerClients < 1 {
		return
	}

	m2 := make(map[string]string)
	m2["changed"] = element
	h.updater <- jsoniffy(m2)
}
<commit_msg>Removed prints<commit_after>package turbowookie

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"
	"time"
)

// TWHandler is our custom http.Handler used to actually do the HTTP stuff.
type TWHandler struct {
	// MpdClient is our MPD Client, used to tell MPD to do things. Important
	// things.
	MpdClient *TWMPDClient

	// ServerConfig is a map of configuration key/values found in
	// a config.yaml file.
	ServerConfig map[string]string

	// Router is a mux.Router, it's what really does all the HTTP stuff, we just
	// act as the interface. And the HandlerFuncs
	Router *mux.Router

	// updater is a channel used by our long poller/polar system. It contains
	// the message of what's been changed.
	updater chan string

	// pollerClients is the number of people currently connected to the long
	// poller.
	pollerClients int
}

// NewTWHandler creates a new TWHandler, using the passed in filename as a
// yaml file containing the server's configuation settings.
func NewTWHandler(filename string, serveDart, startMPD bool, portOverride int) (*TWHandler, error) {
	// make us a pointer to a handler.
	h := &TWHandler{}

	// attempt to read the passed in config file. See `yaml.go` for more info.
	config, err := ReadConfig(filename)
	if err != nil {
		return nil, err
	}

	if !(config["server_port"] != "9000" && portOverride == 9000) {
		config["server_port"] = strconv.Itoa(portOverride)
	}

	h.ServerConfig = config
	h.MpdClient = NewTWMPDClient(h.ServerConfig, startMPD) // see TWMPDClient.go

	// Make sure there's a server to connect to, and run some other startup
	// commands (like making sure there's music playing...).
	err = h.MpdClient.Startup()
	if err != nil {
		log.Fatal("Error running the TWMPDClient startup...\n", err)
	}

	// Actually make our HTTP Router
	h.Router = mux.NewRouter()

	// Let us play the MPD without having to deal with cross origin stuff.
	// Because cross origin is kinda a bitch.
	h.Router.HandleFunc("/stream", httputil.NewSingleHostReverseProxy(
		&url.URL{
			Scheme: "http",
			Host:   h.ServerConfig["mpd_domain"] + ":" + h.ServerConfig["mpd_http_port"],
			Path:   "/",
		}).ServeHTTP)

	h.Router.HandleFunc("/songs", h.listSongs)
	h.Router.HandleFunc("/artists", h.listArtists)
	h.Router.HandleFunc("/albums", h.listArtistAlbums)
	h.Router.HandleFunc("/current", h.getCurrentSong)
	h.Router.HandleFunc("/upcoming", h.getUpcomingSongs)
	h.Router.HandleFunc("/add", h.addSong)
	h.Router.HandleFunc("/polar", h.bear)

	// This needs to be last, otherwise it'll override all routes after it
	// because we're matching EVERYTHING.
	fileDir := h.ServerConfig["turbo_wookie_directory"] + "/frontend/turbo_wookie"
	if serveDart {
		fileDir += "/web"
	} else {
		fileDir += "/build"
	}
	h.Router.PathPrefix("/").Handler(http.FileServer(http.Dir(fileDir)))

	// setup our poller/polar stuff.
	h.updater = make(chan string)
	h.pollerClients = 0

	// nothing bad happened. Suprise!
	return h, nil
}

// Make TWHandler an HTTP.Handler. Hackily. Just pass up the underlying
// Router's function.
func (h *TWHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Router.ServeHTTP(w, r)
}

// HandleFunc make TWHandler extendible...
// Same as ServeHTTP, just pass up the Router's function.
func (h *TWHandler) HandleFunc(path string, f func(w http.ResponseWriter, r *http.Request)) *mux.Route {
	return h.Router.HandleFunc(path, f)
}

// ListenAndServe serve up some TurboWookie. And setup an MPD Watcher to see
// when things happen to the stream. Because things sometimes happen to the
// stream.
func (h *TWHandler) ListenAndServe() error {
	// Setup a watcher.
	WatchMPD(h.ServerConfig["mpd_domain"]+":"+h.ServerConfig["mpd_control_port"], h)

	port := ":" + h.ServerConfig["server_port"]
	log.Println("Starting server on " + port)
	return http.ListenAndServe(port, h)
}

/************************
    HANDLER FUNCTIONS
************************/

// List all songs in the library, and information about those songs.
func (h *TWHandler) listSongs(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	artist, ok1 := r.Form["artist"]
	album, ok2 := r.Form["album"]

	var songs []map[string]string
	var err error

	if ok1 && ok2 {
		songs, err = h.MpdClient.GetSongs(artist[0], album[0])
	} else if ok1 && !ok2 {
		songs, err = h.MpdClient.GetSongs(artist[0], "")
	} else {
		songs, err = h.MpdClient.GetFiles()
	}

	if err != nil {
		printError(w, "An error occured while processing your request", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(songs))
}

func (h *TWHandler) listArtists(w http.ResponseWriter, r *http.Request) {
	artists, err := h.MpdClient.GetArtists()
	if err != nil {
		printError(w, "An error occured while processing your request", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(artists))
}

func (h *TWHandler) listArtistAlbums(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	artist, ok := r.Form["artist"]
	var albums []string
	var err error
	if !ok {
		albums, err = h.MpdClient.GetAlbums("")
	} else {
		albums, err = h.MpdClient.GetAlbums(artist[0])
	}
	if err != nil {
		printError(w, "An error occured while processing your request", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(albums))
}

// Return information about the currently playing song.
func (h *TWHandler) getCurrentSong(w http.ResponseWriter, r *http.Request) {
	currentSong, err := h.MpdClient.CurrentSong()
	if err != nil {
		printError(w, "Couldn't get current song info", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(currentSong))
}

// Return a list of all upcoming songs in the playlist.
// As in, return `playlist[current song + 1 :]`.
func (h *TWHandler) getUpcomingSongs(w http.ResponseWriter, r *http.Request) {
	upcoming, err := h.MpdClient.GetUpcoming()
	if err != nil {
		printError(w, "Couldn't get upcoming playlist", err)
		return
	}

	fmt.Fprintf(w, jsoniffy(upcoming))
}

// Add a song to the playlist. Using the format
//    `/add?song=[FilePath of song]`
func (h *TWHandler) addSong(w http.ResponseWriter, r *http.Request) {
	// for some reason Go doesn't do this automatically.
	r.ParseForm()

	// Get the song from the GET request variables,
	// and check that there's actually something called `song` in the request.
	song, ok := r.Form["song"]
	if !ok {
		printError(w, "No song specified", nil)
		return
	}

	// Attempt to add the song to the playlist
	err := h.MpdClient.Add(song[0])
	if err != nil {
		printError(w, "Unknown song", err)
		return
	}

	// Return a simple note saying that we got the song
	m := make(map[string]string)
	m["note"] = "Added song: " + song[0]
	fmt.Fprintf(w, jsoniffy(m))

	// tell long pollers that the playlist changed.
	h.PolarChanged("playlist")
}

// Our long poller. Accessed through `/polar`.
// Clients connect to this, and wait for either five minutes (after which
// they probably reconnect) or until the server tells them something has
// changed.
//
// This is done so that clients don't need to make periodic requests
// asking for the current playlist.
func (h *TWHandler) bear(w http.ResponseWriter, r *http.Request) {
	// we got another live one.
	h.pollerClients += 1

	// Setup a timeout to make sure the client doesn't sit here forever.
	timeout := make(chan bool)
	defer func() { h.pollerClients -= 1 }()
	go func() {
		// if after five minutes nothing has changed, timeout and have the client
		// connect again.
		time.Sleep(5 * time.Minute)
		timeout <- true
	}()

	// Either the updater has news or the timeout expired.
	// Depending on which, tell the client something or nothing changed.
	select {
	case msg := <-h.updater:
		fmt.Fprintf(w, msg)
		if h.pollerClients > 1 {
			h.updater <- msg
		}
	case <-timeout:
		m := make(map[string]string)
		m["changed"] = "nothing"

		fmt.Fprintf(w, jsoniffy(m))
	}
}

/************************
    HELPER FUNCTIONS
************************/

// Print an error the the screen, and send a simple message to the client.
func printError(w http.ResponseWriter, msg string, err error) {
	log.Println("ERROR:", err)
	log.Println("Sending to client:", msg)
	fmt.Fprintf(w, msg+"\n")
}

// Turn things into JSON.
func jsoniffy(v interface{}) string {
	obj, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		log.Print("Couldn't turn something into JSON: ", v)
		log.Fatal(err)
	}

	return string(obj)
}

// PolarChanged tell clients connected to our long-poll system that something
// (element) has changed.
func (h *TWHandler) PolarChanged(element string) {
	if h.pollerClients < 1 {
		return
	}

	m2 := make(map[string]string)
	m2["changed"] = element
	h.updater <- jsoniffy(m2)
}
<|endoftext|>