<commit_before>package windows_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/cloudfoundry/bosh-agent/agent/action"
	"github.com/cloudfoundry/bosh-agent/integration/windows/utils"
	boshfileutil "github.com/cloudfoundry/bosh-utils/fileutil"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-utils/system"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	agentGUID       = "123-456-789"
	agentID         = "agent." + agentGUID
	senderID        = "director.987-654-321"
	DefaultTimeout  = time.Second * 60
	DefaultInterval = time.Second
)

func natsIP() string {
	if ip := os.Getenv("NATS_PRIVATE_IP"); ip != "" {
		return ip
	}
	return ""
}

func natsURI() string {
	if VagrantProvider == "aws" {
		return fmt.Sprintf("nats://%s:4222", os.Getenv("NATS_ELASTIC_IP"))
	}
	return fmt.Sprintf("nats://%s:4222", natsIP())

}

func blobstoreURI() string {
	if VagrantProvider == "aws" {
		return fmt.Sprintf("http://%s:25250", os.Getenv("NATS_ELASTIC_IP"))
	}
	return fmt.Sprintf("http://%s:25250", natsIP())
}

func getNetworkProperty(key string, natsClient *NatsClient) string {
	message := fmt.Sprintf(`{"method":"get_state","arguments":["full"],"reply_to":"%s"}`, senderID)
	rawResponse, err := natsClient.SendRawMessage(message)
	Expect(err).NotTo(HaveOccurred())

	response := map[string]action.GetStateV1ApplySpec{}
	err = json.Unmarshal(rawResponse, &response)
	Expect(err).NotTo(HaveOccurred())

	for _, spec := range response["value"].NetworkSpecs {
		field, ok := spec.Fields[key]
		if !ok {
			return ""
		}
		if val, ok := field.(string); ok {
			return val
		}
	}
	return ""
}

var _ = Describe("An Agent running on Windows", func() {
	var (
		fs              boshsys.FileSystem
		natsClient      *NatsClient
		blobstoreClient utils.BlobClient
		cmdRunner       boshsys.CmdRunner
	)

	BeforeEach(func() {
		message := fmt.Sprintf(`{"method":"ping","arguments":[],"reply_to":"%s"}`, senderID)

		blobstoreClient = utils.NewBlobstore(blobstoreURI())

		logger := boshlog.NewLogger(boshlog.LevelNone)
		cmdRunner = boshsys.NewExecCmdRunner(logger)
		fs = boshsys.NewOsFileSystem(logger)
		compressor := boshfileutil.NewTarballCompressor(cmdRunner, fs)

		natsClient = NewNatsClient(compressor, blobstoreClient)
		err := natsClient.Setup()
		Expect(err).NotTo(HaveOccurred())

		testPing := func() (string, error) {
			response, err := natsClient.SendRawMessage(message)
			return string(response), err
		}

		Eventually(testPing, DefaultTimeout, DefaultInterval).Should(Equal(`{"value":"pong"}`))
	})

	AfterEach(func() {
		natsClient.Cleanup()
	})

	It("responds to 'get_state' message over NATS", func() {
		getStateSpecAgentID := func() string {
			message := fmt.Sprintf(`{"method":"get_state","arguments":[],"reply_to":"%s"}`, senderID)
			rawResponse, err := natsClient.SendRawMessage(message)

			response := map[string]action.GetStateV1ApplySpec{}
			err = json.Unmarshal(rawResponse, &response)
			Expect(err).NotTo(HaveOccurred())

			return response["value"].AgentID
		}

		Eventually(getStateSpecAgentID, DefaultTimeout, DefaultInterval).Should(Equal(agentGUID))
	})

	It("includes memory vitals in the 'get_state' response", func() {
		message := fmt.Sprintf(`{"method":"get_state","arguments":["full"],"reply_to":"%s"}`, senderID)

		checkVitals := func() error {
			b, err := natsClient.SendRawMessage(message)
			if err != nil {
				return err
			}

			var res map[string]action.GetStateV1ApplySpec
			if err := json.Unmarshal(b, &res); err != nil {
				return err
			}

			v := res["value"].Vitals
			if v == nil {
				return fmt.Errorf("nil Vitals")
			}
			if v.Mem.Kb == "" || v.Mem.Percent == "" {
				return fmt.Errorf("Empty Memory Vitals: %+v", v.Mem)
			}
			return nil
		}

		Eventually(checkVitals, DefaultTimeout, DefaultInterval).Should(Succeed())
	})

	It("can run a run_errand action", func() {
		natsClient.PrepareJob("say-hello")

		runErrandResponse, err := natsClient.RunErrand()
		Expect(err).NotTo(HaveOccurred())

		runErrandCheck := natsClient.CheckErrandResultStatus(runErrandResponse["value"]["agent_task_id"])
		Eventually(runErrandCheck, DefaultTimeout, DefaultInterval).Should(Equal(action.ErrandResult{
			Stdout:     "hello world\r\n",
			ExitStatus: 0,
		}))
	})

	It("can start a job", func() {
		natsClient.PrepareJob("say-hello")

		runStartResponse, err := natsClient.RunStart()
		Expect(err).NotTo(HaveOccurred())
		Expect(runStartResponse["value"]).To(Equal("started"))

		agentState := natsClient.GetState()
		Expect(agentState.JobState).To(Equal("running"))
	})

	It("can run a drain script", func() {
		natsClient.PrepareJob("say-hello")

		err := natsClient.RunDrain()
		Expect(err).NotTo(HaveOccurred())

		logsDir, err := fs.TempDir("windows-agent-drain-test")
		Expect(err).NotTo(HaveOccurred())
		defer fs.RemoveAll(logsDir)

		natsClient.FetchLogs(logsDir)

		drainLogContents, err := fs.ReadFileString(filepath.Join(logsDir, "say-hello", "drain.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(drainLogContents).To(ContainSubstring("Hello from drain"))
	})

	It("can unmonitor the job during drain script", func() {
		natsClient.PrepareJob("unmonitor-hello")

		runStartResponse, err := natsClient.RunStart()
		Expect(err).NotTo(HaveOccurred())
		Expect(runStartResponse["value"]).To(Equal("started"))

		agentState := natsClient.GetState()
		Expect(agentState.JobState).To(Equal("running"))

		err = natsClient.RunDrain()
		Expect(err).NotTo(HaveOccurred())

		logsDir, err := fs.TempDir("windows-agent-drain-test")
		Expect(err).NotTo(HaveOccurred())
		defer fs.RemoveAll(logsDir)

		natsClient.FetchLogs(logsDir)

		drainLogContents, err := fs.ReadFileString(filepath.Join(logsDir, "unmonitor-hello", "drain.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(drainLogContents).To(ContainSubstring("success"))
	})

	It("stops alerting failing jobs when job is stopped", func() {
		natsClient.PrepareJob("crashes-on-start")
		runStartResponse, err := natsClient.RunStart()
		Expect(err).NotTo(HaveOccurred())
		Expect(runStartResponse["value"]).To(Equal("started"))

		Eventually(func() string {
			return natsClient.GetState().JobState
		}, DefaultTimeout, DefaultInterval).Should(Equal("failing"))

		Eventually(func() (string, error) {
			alert, err := natsClient.GetNextAlert(10 * time.Second)
			if err != nil {
				return "", err
			}
			return alert.Title, nil
		}).Should(MatchRegexp(`crash-service \(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\) - pid failed - Start`))

		err = natsClient.RunStop()
		Expect(err).NotTo(HaveOccurred())

		_, err = natsClient.GetNextAlert(10 * time.Second)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("nats: timeout"))
	})

	It("can run arbitrary user scripts", func() {
		natsClient.PrepareJob("say-hello")

		err := natsClient.RunScript("pre-start")
		Expect(err).NotTo(HaveOccurred())

		logsDir, err := fs.TempDir("windows-agent-prestart-test")
		Expect(err).NotTo(HaveOccurred())
		defer fs.RemoveAll(logsDir)

		natsClient.FetchLogs(logsDir)

		prestartStdoutContents, err := fs.ReadFileString(filepath.Join(logsDir, "say-hello", "pre-start.stdout.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(prestartStdoutContents).To(ContainSubstring("Hello from stdout"))

		prestartStderrContents, err := fs.ReadFileString(filepath.Join(logsDir, "say-hello", "pre-start.stderr.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(prestartStderrContents).To(ContainSubstring("Hello from stderr"))
	})

	It("can compile packages", func() {
		const (
			blobName     = "blob.tar"
			fileName     = "output.txt"
			fileContents = "i'm a compiled package!"
		)
		result, err := natsClient.CompilePackage("simple-package")
		Expect(err).NotTo(HaveOccurred())

		tempDir, err := fs.TempDir("windows-agent-compile-test")
		Expect(err).NotTo(HaveOccurred())

		path := filepath.Join(tempDir, blobName)
		Expect(blobstoreClient.Get(result.BlobstoreID, path)).To(Succeed())

		tarPath, err := ioutil.TempDir("", "")
		Expect(err).NotTo(HaveOccurred())
		defer os.Remove(tarPath)

		err = exec.Command("tar", "xf", path, "-C", tarPath).Run()
		Expect(err).NotTo(HaveOccurred())

		out, err := ioutil.ReadFile(filepath.Join(tarPath, fileName))
		Expect(err).NotTo(HaveOccurred())
		Expect(string(out)).To(ContainSubstring(fileContents))
	})

	It("Includes the default IP in the 'get_state' response", func() {
		getNetwork := func(key string) string {
			return getNetworkProperty(key, natsClient)
		}
		Eventually(getNetwork("ip"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
		Eventually(getNetwork("gateway"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
		Eventually(getNetwork("netmask"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
	})

	It("can compile longpath complex pakcage", func() {
		const (
			blobName     = "blob.tar"
			fileName     = "output.txt"
			fileContents = "i'm a compiled package!"
		)
		_, err := natsClient.CompilePackage("longpath-package")
		Expect(err).NotTo(HaveOccurred())
	})

	Context("when there are multiple networks", func() {
		BeforeEach(func() {
			if OsVersion == "2012R2" {
				Skip("Cannot create virtual interfaces in 2012R2")
			}
			natsClient.PrepareJob("add-network-interface")

			err := natsClient.RunScript("run")
			Expect(err).NotTo(HaveOccurred())
		})

		AfterEach(func() {
			natsClient.PrepareJob("remove-network-interface")

			err := natsClient.RunScript("run")
			Expect(err).NotTo(HaveOccurred())
		})

		It("chooses correct IP for default network", func() {
			getNetwork := func(key string) string {
				return getNetworkProperty(key, natsClient)
			}
			Eventually(getNetwork("ip"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
			Expect(getNetwork("ip")).ToNot(HavePrefix("172."))
		})
	})

	It("can reset the system time using an NTP server", func() {
		_, _, _, _ = cmdRunner.RunCommand("time", "12:12:12.0")
		_, _, _, _ = cmdRunner.RunCommand("w32tm", "/resync")

		stdout, stderr, exitCode, err := cmdRunner.RunCommand("powershell.exe", "time", "/t")

		Expect(stdout).To(Equal("TEST_FAIL 12:12 PM"))
		Expect(stderr).To(BeEmpty())
		Expect(exitCode).To(Equal(0))
		Expect(err).NotTo(HaveOccurred())
	})
})
<commit_msg>Revert "Wrap the `time` command in powershell"<commit_after>package windows_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/cloudfoundry/bosh-agent/agent/action"
	"github.com/cloudfoundry/bosh-agent/integration/windows/utils"
	boshfileutil "github.com/cloudfoundry/bosh-utils/fileutil"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-utils/system"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	agentGUID       = "123-456-789"
	agentID         = "agent." + agentGUID
	senderID        = "director.987-654-321"
	DefaultTimeout  = time.Second * 60
	DefaultInterval = time.Second
)

func natsIP() string {
	if ip := os.Getenv("NATS_PRIVATE_IP"); ip != "" {
		return ip
	}
	return ""
}

func natsURI() string {
	if VagrantProvider == "aws" {
		return fmt.Sprintf("nats://%s:4222", os.Getenv("NATS_ELASTIC_IP"))
	}
	return fmt.Sprintf("nats://%s:4222", natsIP())

}

func blobstoreURI() string {
	if VagrantProvider == "aws" {
		return fmt.Sprintf("http://%s:25250", os.Getenv("NATS_ELASTIC_IP"))
	}
	return fmt.Sprintf("http://%s:25250", natsIP())
}

func getNetworkProperty(key string, natsClient *NatsClient) string {
	message := fmt.Sprintf(`{"method":"get_state","arguments":["full"],"reply_to":"%s"}`, senderID)
	rawResponse, err := natsClient.SendRawMessage(message)
	Expect(err).NotTo(HaveOccurred())

	response := map[string]action.GetStateV1ApplySpec{}
	err = json.Unmarshal(rawResponse, &response)
	Expect(err).NotTo(HaveOccurred())

	for _, spec := range response["value"].NetworkSpecs {
		field, ok := spec.Fields[key]
		if !ok {
			return ""
		}
		if val, ok := field.(string); ok {
			return val
		}
	}
	return ""
}

var _ = Describe("An Agent running on Windows", func() {
	var (
		fs              boshsys.FileSystem
		natsClient      *NatsClient
		blobstoreClient utils.BlobClient
		cmdRunner       boshsys.CmdRunner
	)

	BeforeEach(func() {
		message := fmt.Sprintf(`{"method":"ping","arguments":[],"reply_to":"%s"}`, senderID)

		blobstoreClient = utils.NewBlobstore(blobstoreURI())

		logger := boshlog.NewLogger(boshlog.LevelNone)
		cmdRunner = boshsys.NewExecCmdRunner(logger)
		fs = boshsys.NewOsFileSystem(logger)
		compressor := boshfileutil.NewTarballCompressor(cmdRunner, fs)

		natsClient = NewNatsClient(compressor, blobstoreClient)
		err := natsClient.Setup()
		Expect(err).NotTo(HaveOccurred())

		testPing := func() (string, error) {
			response, err := natsClient.SendRawMessage(message)
			return string(response), err
		}

		Eventually(testPing, DefaultTimeout, DefaultInterval).Should(Equal(`{"value":"pong"}`))
	})

	AfterEach(func() {
		natsClient.Cleanup()
	})

	It("responds to 'get_state' message over NATS", func() {
		getStateSpecAgentID := func() string {
			message := fmt.Sprintf(`{"method":"get_state","arguments":[],"reply_to":"%s"}`, senderID)
			rawResponse, err := natsClient.SendRawMessage(message)

			response := map[string]action.GetStateV1ApplySpec{}
			err = json.Unmarshal(rawResponse, &response)
			Expect(err).NotTo(HaveOccurred())

			return response["value"].AgentID
		}

		Eventually(getStateSpecAgentID, DefaultTimeout, DefaultInterval).Should(Equal(agentGUID))
	})

	It("includes memory vitals in the 'get_state' response", func() {
		message := fmt.Sprintf(`{"method":"get_state","arguments":["full"],"reply_to":"%s"}`, senderID)

		checkVitals := func() error {
			b, err := natsClient.SendRawMessage(message)
			if err != nil {
				return err
			}

			var res map[string]action.GetStateV1ApplySpec
			if err := json.Unmarshal(b, &res); err != nil {
				return err
			}

			v := res["value"].Vitals
			if v == nil {
				return fmt.Errorf("nil Vitals")
			}
			if v.Mem.Kb == "" || v.Mem.Percent == "" {
				return fmt.Errorf("Empty Memory Vitals: %+v", v.Mem)
			}
			return nil
		}

		Eventually(checkVitals, DefaultTimeout, DefaultInterval).Should(Succeed())
	})

	It("can run a run_errand action", func() {
		natsClient.PrepareJob("say-hello")

		runErrandResponse, err := natsClient.RunErrand()
		Expect(err).NotTo(HaveOccurred())

		runErrandCheck := natsClient.CheckErrandResultStatus(runErrandResponse["value"]["agent_task_id"])
		Eventually(runErrandCheck, DefaultTimeout, DefaultInterval).Should(Equal(action.ErrandResult{
			Stdout:     "hello world\r\n",
			ExitStatus: 0,
		}))
	})

	It("can start a job", func() {
		natsClient.PrepareJob("say-hello")

		runStartResponse, err := natsClient.RunStart()
		Expect(err).NotTo(HaveOccurred())
		Expect(runStartResponse["value"]).To(Equal("started"))

		agentState := natsClient.GetState()
		Expect(agentState.JobState).To(Equal("running"))
	})

	It("can run a drain script", func() {
		natsClient.PrepareJob("say-hello")

		err := natsClient.RunDrain()
		Expect(err).NotTo(HaveOccurred())

		logsDir, err := fs.TempDir("windows-agent-drain-test")
		Expect(err).NotTo(HaveOccurred())
		defer fs.RemoveAll(logsDir)

		natsClient.FetchLogs(logsDir)

		drainLogContents, err := fs.ReadFileString(filepath.Join(logsDir, "say-hello", "drain.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(drainLogContents).To(ContainSubstring("Hello from drain"))
	})

	It("can unmonitor the job during drain script", func() {
		natsClient.PrepareJob("unmonitor-hello")

		runStartResponse, err := natsClient.RunStart()
		Expect(err).NotTo(HaveOccurred())
		Expect(runStartResponse["value"]).To(Equal("started"))

		agentState := natsClient.GetState()
		Expect(agentState.JobState).To(Equal("running"))

		err = natsClient.RunDrain()
		Expect(err).NotTo(HaveOccurred())

		logsDir, err := fs.TempDir("windows-agent-drain-test")
		Expect(err).NotTo(HaveOccurred())
		defer fs.RemoveAll(logsDir)

		natsClient.FetchLogs(logsDir)

		drainLogContents, err := fs.ReadFileString(filepath.Join(logsDir, "unmonitor-hello", "drain.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(drainLogContents).To(ContainSubstring("success"))
	})

	It("stops alerting failing jobs when job is stopped", func() {
		natsClient.PrepareJob("crashes-on-start")
		runStartResponse, err := natsClient.RunStart()
		Expect(err).NotTo(HaveOccurred())
		Expect(runStartResponse["value"]).To(Equal("started"))

		Eventually(func() string {
			return natsClient.GetState().JobState
		}, DefaultTimeout, DefaultInterval).Should(Equal("failing"))

		Eventually(func() (string, error) {
			alert, err := natsClient.GetNextAlert(10 * time.Second)
			if err != nil {
				return "", err
			}
			return alert.Title, nil
		}).Should(MatchRegexp(`crash-service \(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\) - pid failed - Start`))

		err = natsClient.RunStop()
		Expect(err).NotTo(HaveOccurred())

		_, err = natsClient.GetNextAlert(10 * time.Second)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("nats: timeout"))
	})

	It("can run arbitrary user scripts", func() {
		natsClient.PrepareJob("say-hello")

		err := natsClient.RunScript("pre-start")
		Expect(err).NotTo(HaveOccurred())

		logsDir, err := fs.TempDir("windows-agent-prestart-test")
		Expect(err).NotTo(HaveOccurred())
		defer fs.RemoveAll(logsDir)

		natsClient.FetchLogs(logsDir)

		prestartStdoutContents, err := fs.ReadFileString(filepath.Join(logsDir, "say-hello", "pre-start.stdout.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(prestartStdoutContents).To(ContainSubstring("Hello from stdout"))

		prestartStderrContents, err := fs.ReadFileString(filepath.Join(logsDir, "say-hello", "pre-start.stderr.log"))
		Expect(err).NotTo(HaveOccurred())

		Expect(prestartStderrContents).To(ContainSubstring("Hello from stderr"))
	})

	It("can compile packages", func() {
		const (
			blobName     = "blob.tar"
			fileName     = "output.txt"
			fileContents = "i'm a compiled package!"
		)
		result, err := natsClient.CompilePackage("simple-package")
		Expect(err).NotTo(HaveOccurred())

		tempDir, err := fs.TempDir("windows-agent-compile-test")
		Expect(err).NotTo(HaveOccurred())

		path := filepath.Join(tempDir, blobName)
		Expect(blobstoreClient.Get(result.BlobstoreID, path)).To(Succeed())

		tarPath, err := ioutil.TempDir("", "")
		Expect(err).NotTo(HaveOccurred())
		defer os.Remove(tarPath)

		err = exec.Command("tar", "xf", path, "-C", tarPath).Run()
		Expect(err).NotTo(HaveOccurred())

		out, err := ioutil.ReadFile(filepath.Join(tarPath, fileName))
		Expect(err).NotTo(HaveOccurred())
		Expect(string(out)).To(ContainSubstring(fileContents))
	})

	It("Includes the default IP in the 'get_state' response", func() {
		getNetwork := func(key string) string {
			return getNetworkProperty(key, natsClient)
		}
		Eventually(getNetwork("ip"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
		Eventually(getNetwork("gateway"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
		Eventually(getNetwork("netmask"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
	})

	It("can compile longpath complex pakcage", func() {
		const (
			blobName     = "blob.tar"
			fileName     = "output.txt"
			fileContents = "i'm a compiled package!"
		)
		_, err := natsClient.CompilePackage("longpath-package")
		Expect(err).NotTo(HaveOccurred())
	})

	Context("when there are multiple networks", func() {
		BeforeEach(func() {
			if OsVersion == "2012R2" {
				Skip("Cannot create virtual interfaces in 2012R2")
			}
			natsClient.PrepareJob("add-network-interface")

			err := natsClient.RunScript("run")
			Expect(err).NotTo(HaveOccurred())
		})

		AfterEach(func() {
			natsClient.PrepareJob("remove-network-interface")

			err := natsClient.RunScript("run")
			Expect(err).NotTo(HaveOccurred())
		})

		It("chooses correct IP for default network", func() {
			getNetwork := func(key string) string {
				return getNetworkProperty(key, natsClient)
			}
			Eventually(getNetwork("ip"), DefaultTimeout, DefaultInterval).ShouldNot(BeEmpty())
			Expect(getNetwork("ip")).ToNot(HavePrefix("172."))
		})
	})

	It("can reset the system time using an NTP server", func() {
		_, _, _, _ = cmdRunner.RunCommand("time", "12:12:12.0")
		_, _, _, _ = cmdRunner.RunCommand("w32tm", "/resync")

		stdout, stderr, exitCode, err := cmdRunner.RunCommand("time", "/t")

		Expect(stdout).To(Equal("TEST_FAIL 12:12 PM"))
		Expect(stderr).To(BeEmpty())
		Expect(exitCode).To(Equal(0))
		Expect(err).NotTo(HaveOccurred())
	})
})
<|endoftext|>


<commit_before>package fusefrontend

import (
	"sync"
)

func init() {
	wlock.m = make(map[uint64]*refCntMutex)
}

// wlock - serializes write accesses to each file (identified by inode number)
// Writing partial blocks means we have to do read-modify-write cycles. We
// really don't want concurrent writes there.
// Concurrent full-block writes could actually be allowed, but are not to
// keep the locking simple.
var wlock wlockMap

// wlockMap - usage:
// 1) register
// 2) lock ... unlock ...
// 3) unregister
type wlockMap struct {
	mapMutex sync.RWMutex
	m        map[uint64]*refCntMutex
}

func (w *wlockMap) register(ino uint64) {
	w.mapMutex.Lock()
	r := w.m[ino]
	if r == nil {
		r = &refCntMutex{}
		w.m[ino] = r
	}
	r.refCnt++ // this must happen inside the mapMutex lock
	w.mapMutex.Unlock()
}

func (w *wlockMap) unregister(ino uint64) {
	w.mapMutex.Lock()
	r := w.m[ino]
	r.refCnt--
	if r.refCnt == 0 {
		delete(w.m, ino)
	}
	w.mapMutex.Unlock()
}

func (w *wlockMap) lock(ino uint64) {
	w.mapMutex.RLock()
	r := w.m[ino]
	w.mapMutex.RUnlock()
	r.Lock() // this can take a long time - execute outside the mapMutex lock
}

func (w *wlockMap) unlock(ino uint64) {
	w.mapMutex.RLock()
	r := w.m[ino]
	w.mapMutex.RUnlock()
	r.Unlock()
}

// refCntMutex - mutex with reference count
type refCntMutex struct {
	sync.Mutex
	refCnt int
}
<commit_msg>fusefrontend: simplify wlockMap<commit_after>package fusefrontend

import (
	"sync"
)

func init() {
	wlock.inodeLocks = make(map[uint64]*refCntMutex)
}

// wlock - serializes write accesses to each file (identified by inode number)
// Writing partial blocks means we have to do read-modify-write cycles. We
// really don't want concurrent writes there.
// Concurrent full-block writes could actually be allowed, but are not to
// keep the locking simple.
var wlock wlockMap

// wlockMap - usage:
// 1) register
// 2) lock ... unlock ...
// 3) unregister
type wlockMap struct {
	// Protects map access
	sync.Mutex
	inodeLocks map[uint64]*refCntMutex
}

// refCntMutex - mutex with reference count
type refCntMutex struct {
	// Write lock for this inode
	sync.Mutex
	// Reference count
	refCnt int
}

// register creates an entry for "ino", or incrementes the reference count
// if the entry already exists.
func (w *wlockMap) register(ino uint64) {
	w.Lock()
	defer w.Unlock()

	r := w.inodeLocks[ino]
	if r == nil {
		r = &refCntMutex{}
		w.inodeLocks[ino] = r
	}
	r.refCnt++
}

// unregister decrements the reference count for "ino" and deletes the entry if
// the reference count has reached 0.
func (w *wlockMap) unregister(ino uint64) {
	w.Lock()
	defer w.Unlock()

	r := w.inodeLocks[ino]
	r.refCnt--
	if r.refCnt == 0 {
		delete(w.inodeLocks, ino)
	}
}

// lock retrieves the entry for "ino" and locks it.
func (w *wlockMap) lock(ino uint64) {
	w.Lock()
	r := w.inodeLocks[ino]
	w.Unlock()
	// this can take a long time - execute outside the wlockMap lock
	r.Lock()
}

// unlock retrieves the entry for "ino" and unlocks it.
func (w *wlockMap) unlock(ino uint64) {
	w.Lock()
	r := w.inodeLocks[ino]
	w.Unlock()
	r.Unlock()
}
<|endoftext|>


<commit_before>package loraserver

import (
	"bytes"
	"crypto/aes"
	"crypto/rand"
	"encoding/gob"
	"encoding/hex"
	"fmt"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/brocaar/loraserver/models"
	"github.com/brocaar/lorawan"
	"github.com/garyburd/redigo/redis"
)

// NodeSession related constants
const (
	NodeSessionTTL = time.Hour * 24 * 5 // TTL of a node session (will be renewed on each activity)
)

const (
	nodeSessionKeyTempl = "node_session_%s"
)

// createNodeSession does the same as saveNodeSession except that it does not
// overwrite an exisitng record.
func createNodeSession(p *redis.Pool, s models.NodeSession) error {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(s); err != nil {
		return fmt.Errorf("encode node-session for node %s error: %s", s.DevEUI, err)
	}

	c := p.Get()
	defer c.Close()

	exp := int64(NodeSessionTTL) / int64(time.Millisecond)

	if _, err := redis.String(c.Do("SET", fmt.Sprintf(nodeSessionKeyTempl, s.DevAddr), buf.Bytes(), "NX", "PX", exp)); err != nil {
		return fmt.Errorf("create node-session %s for node %s error: %s", s.DevAddr, s.DevEUI, err)
	}
	// DevEUI -> DevAddr pointer
	if _, err := redis.String(c.Do("PSETEX", fmt.Sprintf(nodeSessionKeyTempl, s.DevEUI), exp, s.DevAddr.String())); err != nil {
		return fmt.Errorf("create pointer node %s -> DevAddr %s error: %s", s.DevEUI, s.DevAddr, err)
	}

	log.WithField("dev_addr", s.DevAddr).Info("node-session created")
	return nil
}

// saveNodeSession saves the node session. Note that the session will automatically
// expire after NodeSessionTTL.
func saveNodeSession(p *redis.Pool, s models.NodeSession) error {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(s); err != nil {
		return fmt.Errorf("encode node-session for node %s error: %s", s.DevEUI, err)
	}

	c := p.Get()
	defer c.Close()

	exp := int64(NodeSessionTTL) / int64(time.Millisecond)

	if _, err := redis.String(c.Do("PSETEX", fmt.Sprintf(nodeSessionKeyTempl, s.DevAddr), exp, buf.Bytes())); err != nil {
		return fmt.Errorf("save node-session %s for node %s error: %s", s.DevAddr, s.DevEUI, err)
	}
	// DevEUI -> DevAddr pointer
	if _, err := redis.String(c.Do("PSETEX", fmt.Sprintf(nodeSessionKeyTempl, s.DevEUI), exp, s.DevAddr.String())); err != nil {
		return fmt.Errorf("create pointer node %s -> DevAddr %s error: %s", s.DevEUI, s.DevAddr, err)
	}

	log.WithField("dev_addr", s.DevAddr).Info("node-session saved")
	return nil
}

// getNodeSession returns the NodeSession for the given DevAddr.
func getNodeSession(p *redis.Pool, devAddr lorawan.DevAddr) (models.NodeSession, error) {
	var ns models.NodeSession

	c := p.Get()
	defer c.Close()

	val, err := redis.Bytes(c.Do("GET", fmt.Sprintf(nodeSessionKeyTempl, devAddr)))
	if err != nil {
		return ns, fmt.Errorf("get node-session for DevAddr %s error: %s", devAddr, err)
	}

	err = gob.NewDecoder(bytes.NewReader(val)).Decode(&ns)
	if err != nil {
		return ns, fmt.Errorf("decode node-session %s error: %s", devAddr, err)
	}

	return ns, nil
}

// getNodeSessionByDevEUI returns the NodeSession for the given DevEUI.
func getNodeSessionByDevEUI(p *redis.Pool, devEUI lorawan.EUI64) (models.NodeSession, error) {
	var ns models.NodeSession

	c := p.Get()
	defer c.Close()

	devAddr, err := redis.String(c.Do("GET", fmt.Sprintf(nodeSessionKeyTempl, devEUI)))
	if err != nil {
		return ns, fmt.Errorf("get node-session pointer for node %s error: %s", devEUI, err)
	}

	val, err := redis.Bytes(c.Do("GET", fmt.Sprintf(nodeSessionKeyTempl, devAddr)))
	if err != nil {
		return ns, fmt.Errorf("get node-session for DevAddr %s error: %s", devAddr, err)
	}

	err = gob.NewDecoder(bytes.NewReader(val)).Decode(&ns)
	if err != nil {
		return ns, fmt.Errorf("decode node-session %s error: %s", devAddr, err)
	}

	return ns, nil
}

// deleteNodeSession deletes the NodeSession matching the given DevAddr.
func deleteNodeSession(p *redis.Pool, devAddr lorawan.DevAddr) error {
	c := p.Get()
	defer c.Close()

	val, err := redis.Int(c.Do("DEL", fmt.Sprintf(nodeSessionKeyTempl, devAddr)))
	if err != nil {
		return fmt.Errorf("delete node-session %s error: %s", devAddr, err)
	}
	if val == 0 {
		return fmt.Errorf("node-session %s does not exist", devAddr)
	}
	log.WithField("dev_addr", devAddr).Info("node-session deleted")
	return nil
}

// getRandomDevAddr returns a random free DevAddr. Note that the 7 MSB will be
// set to the NwkID (based on the configured NetID).
// TODO: handle collission with retry?
func getRandomDevAddr(p *redis.Pool, netID lorawan.NetID) (lorawan.DevAddr, error) {
	var d lorawan.DevAddr
	b := make([]byte, len(d))
	if _, err := rand.Read(b); err != nil {
		return d, fmt.Errorf("could not read from random reader: %s", err)
	}
	copy(d[:], b)
	d[0] = d[0] & 1                    // zero out 7 msb
	d[0] = d[0] ^ (netID.NwkID() << 1) // set 7 msb to NwkID

	c := p.Get()
	defer c.Close()

	key := "node_session_" + d.String()
	val, err := redis.Int(c.Do("EXISTS", key))
	if err != nil {
		return lorawan.DevAddr{}, fmt.Errorf("test DevAddr %s exist error: %s", d, err)
	}
	if val == 1 {
		return lorawan.DevAddr{}, fmt.Errorf("DevAddr %s already exists", d)
	}
	return d, nil
}

// getAppNonce returns a random application nonce (used for OTAA).
func getAppNonce() ([3]byte, error) {
	var b [3]byte
	if _, err := rand.Read(b[:]); err != nil {
		return b, err
	}
	return b, nil
}

// getNwkSKey returns the network session key.
func getNwkSKey(appkey lorawan.AES128Key, netID lorawan.NetID, appNonce [3]byte, devNonce [2]byte) (lorawan.AES128Key, error) {
	return getSKey(0x01, appkey, netID, appNonce, devNonce)
}

// getAppSKey returns the application session key.
func getAppSKey(appkey lorawan.AES128Key, netID lorawan.NetID, appNonce [3]byte, devNonce [2]byte) (lorawan.AES128Key, error) {
	return getSKey(0x02, appkey, netID, appNonce, devNonce)
}

func getSKey(typ byte, appkey lorawan.AES128Key, netID lorawan.NetID, appNonce [3]byte, devNonce [2]byte) (lorawan.AES128Key, error) {
	var key lorawan.AES128Key
	b := make([]byte, 0, 16)
	b = append(b, typ)

	// little endian
	for i := len(appNonce) - 1; i >= 0; i-- {
		b = append(b, appNonce[i])
	}
	for i := len(netID) - 1; i >= 0; i-- {
		b = append(b, netID[i])
	}
	for i := len(devNonce) - 1; i >= 0; i-- {
		b = append(b, devNonce[i])
	}
	pad := make([]byte, 7)
	b = append(b, pad...)

	block, err := aes.NewCipher(appkey[:])
	if err != nil {
		return key, err
	}
	if block.BlockSize() != len(b) {
		return key, fmt.Errorf("block-size of %d bytes is expected", len(b))
	}
	block.Encrypt(key[:], b)
	return key, nil
}

// NodeSessionAPI exports the NodeSession related functions.
type NodeSessionAPI struct {
	ctx Context
}

// NewNodeSessionAPI crestes a new NodeSessionAPI.
func NewNodeSessionAPI(ctx Context) *NodeSessionAPI {
	return &NodeSessionAPI{
		ctx: ctx,
	}
}

// Get returns the NodeSession for the given DevAddr.
func (a *NodeSessionAPI) Get(devAddr lorawan.DevAddr, ns *models.NodeSession) error {
	var err error
	*ns, err = getNodeSession(a.ctx.RedisPool, devAddr)
	return err
}

// GetByDevEUI returns the NodeSession for the given DevEUI.
func (a *NodeSessionAPI) GetByDevEUI(devEUI lorawan.EUI64, ns *models.NodeSession) error {
	var err error
	*ns, err = getNodeSessionByDevEUI(a.ctx.RedisPool, devEUI)
	return err
}

// Create creates the given NodeSession (activation by personalization).
// The DevAddr must contain the same NwkID as the configured NetID.
// Sessions will expire automatically after the configured TTL.
func (a *NodeSessionAPI) Create(ns models.NodeSession, devAddr *lorawan.DevAddr) error {
	// validate the NwkID
	if ns.DevAddr.NwkID() != a.ctx.NetID.NwkID() {
		return fmt.Errorf("DevAddr must contain NwkID %s", hex.EncodeToString([]byte{a.ctx.NetID.NwkID()}))
	}

	// validate that the node exists
	var node models.Node
	var err error
	if node, err = getNode(a.ctx.DB, ns.DevEUI); err != nil {
		return err
	}

	if ns.AppEUI != node.AppEUI {
		return fmt.Errorf("DevEUI %s belongs to AppEUI %s, got AppEUI %s", ns.AppEUI, node.AppEUI, ns.AppEUI)
	}

	if err = createNodeSession(a.ctx.RedisPool, ns); err != nil {
		return err
	}
	*devAddr = ns.DevAddr
	return nil
}

// Update updates the given NodeSession.
func (a *NodeSessionAPI) Update(ns models.NodeSession, devEUI *lorawan.EUI64) error {
	if err := saveNodeSession(a.ctx.RedisPool, ns); err != nil {
		return err
	}
	*devEUI = ns.DevEUI
	return nil
}

// Delete the NodeSession matching the given DevAddr.
func (a *NodeSessionAPI) Delete(devAddr lorawan.DevAddr, deletedDevAddr *lorawan.DevAddr) error {
	if err := deleteNodeSession(a.ctx.RedisPool, devAddr); err != nil {
		return err
	}
	*deletedDevAddr = devAddr
	return nil
}

// GetRandomDevAddr returns a random DevAddr.
func (a *NodeSessionAPI) GetRandomDevAddr(dummy interface{}, devAddr *lorawan.DevAddr) error {
	var err error
	*devAddr, err = getRandomDevAddr(a.ctx.RedisPool, a.ctx.NetID)
	return err
}
<commit_msg>Validate NwkID on node-session update.<commit_after>package loraserver

import (
	"bytes"
	"crypto/aes"
	"crypto/rand"
	"encoding/gob"
	"encoding/hex"
	"fmt"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/brocaar/loraserver/models"
	"github.com/brocaar/lorawan"
	"github.com/garyburd/redigo/redis"
)

// NodeSession related constants
const (
	NodeSessionTTL = time.Hour * 24 * 5 // TTL of a node session (will be renewed on each activity)
)

const (
	nodeSessionKeyTempl = "node_session_%s"
)

// createNodeSession does the same as saveNodeSession except that it does not
// overwrite an exisitng record.
func createNodeSession(p *redis.Pool, s models.NodeSession) error {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(s); err != nil {
		return fmt.Errorf("encode node-session for node %s error: %s", s.DevEUI, err)
	}

	c := p.Get()
	defer c.Close()

	exp := int64(NodeSessionTTL) / int64(time.Millisecond)

	if _, err := redis.String(c.Do("SET", fmt.Sprintf(nodeSessionKeyTempl, s.DevAddr), buf.Bytes(), "NX", "PX", exp)); err != nil {
		return fmt.Errorf("create node-session %s for node %s error: %s", s.DevAddr, s.DevEUI, err)
	}
	// DevEUI -> DevAddr pointer
	if _, err := redis.String(c.Do("PSETEX", fmt.Sprintf(nodeSessionKeyTempl, s.DevEUI), exp, s.DevAddr.String())); err != nil {
		return fmt.Errorf("create pointer node %s -> DevAddr %s error: %s", s.DevEUI, s.DevAddr, err)
	}

	log.WithField("dev_addr", s.DevAddr).Info("node-session created")
	return nil
}

// saveNodeSession saves the node session. Note that the session will automatically
// expire after NodeSessionTTL.
func saveNodeSession(p *redis.Pool, s models.NodeSession) error {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(s); err != nil {
		return fmt.Errorf("encode node-session for node %s error: %s", s.DevEUI, err)
	}

	c := p.Get()
	defer c.Close()

	exp := int64(NodeSessionTTL) / int64(time.Millisecond)

	if _, err := redis.String(c.Do("PSETEX", fmt.Sprintf(nodeSessionKeyTempl, s.DevAddr), exp, buf.Bytes())); err != nil {
		return fmt.Errorf("save node-session %s for node %s error: %s", s.DevAddr, s.DevEUI, err)
	}
	// DevEUI -> DevAddr pointer
	if _, err := redis.String(c.Do("PSETEX", fmt.Sprintf(nodeSessionKeyTempl, s.DevEUI), exp, s.DevAddr.String())); err != nil {
		return fmt.Errorf("create pointer node %s -> DevAddr %s error: %s", s.DevEUI, s.DevAddr, err)
	}

	log.WithField("dev_addr", s.DevAddr).Info("node-session saved")
	return nil
}

// getNodeSession returns the NodeSession for the given DevAddr.
func getNodeSession(p *redis.Pool, devAddr lorawan.DevAddr) (models.NodeSession, error) {
	var ns models.NodeSession

	c := p.Get()
	defer c.Close()

	val, err := redis.Bytes(c.Do("GET", fmt.Sprintf(nodeSessionKeyTempl, devAddr)))
	if err != nil {
		return ns, fmt.Errorf("get node-session for DevAddr %s error: %s", devAddr, err)
	}

	err = gob.NewDecoder(bytes.NewReader(val)).Decode(&ns)
	if err != nil {
		return ns, fmt.Errorf("decode node-session %s error: %s", devAddr, err)
	}

	return ns, nil
}

// getNodeSessionByDevEUI returns the NodeSession for the given DevEUI.
func getNodeSessionByDevEUI(p *redis.Pool, devEUI lorawan.EUI64) (models.NodeSession, error) {
	var ns models.NodeSession

	c := p.Get()
	defer c.Close()

	devAddr, err := redis.String(c.Do("GET", fmt.Sprintf(nodeSessionKeyTempl, devEUI)))
	if err != nil {
		return ns, fmt.Errorf("get node-session pointer for node %s error: %s", devEUI, err)
	}

	val, err := redis.Bytes(c.Do("GET", fmt.Sprintf(nodeSessionKeyTempl, devAddr)))
	if err != nil {
		return ns, fmt.Errorf("get node-session for DevAddr %s error: %s", devAddr, err)
	}

	err = gob.NewDecoder(bytes.NewReader(val)).Decode(&ns)
	if err != nil {
		return ns, fmt.Errorf("decode node-session %s error: %s", devAddr, err)
	}

	return ns, nil
}

// deleteNodeSession deletes the NodeSession matching the given DevAddr.
func deleteNodeSession(p *redis.Pool, devAddr lorawan.DevAddr) error {
	c := p.Get()
	defer c.Close()

	val, err := redis.Int(c.Do("DEL", fmt.Sprintf(nodeSessionKeyTempl, devAddr)))
	if err != nil {
		return fmt.Errorf("delete node-session %s error: %s", devAddr, err)
	}
	if val == 0 {
		return fmt.Errorf("node-session %s does not exist", devAddr)
	}
	log.WithField("dev_addr", devAddr).Info("node-session deleted")
	return nil
}

// getRandomDevAddr returns a random free DevAddr. Note that the 7 MSB will be
// set to the NwkID (based on the configured NetID).
// TODO: handle collission with retry?
func getRandomDevAddr(p *redis.Pool, netID lorawan.NetID) (lorawan.DevAddr, error) {
	var d lorawan.DevAddr
	b := make([]byte, len(d))
	if _, err := rand.Read(b); err != nil {
		return d, fmt.Errorf("could not read from random reader: %s", err)
	}
	copy(d[:], b)
	d[0] = d[0] & 1                    // zero out 7 msb
	d[0] = d[0] ^ (netID.NwkID() << 1) // set 7 msb to NwkID

	c := p.Get()
	defer c.Close()

	key := "node_session_" + d.String()
	val, err := redis.Int(c.Do("EXISTS", key))
	if err != nil {
		return lorawan.DevAddr{}, fmt.Errorf("test DevAddr %s exist error: %s", d, err)
	}
	if val == 1 {
		return lorawan.DevAddr{}, fmt.Errorf("DevAddr %s already exists", d)
	}
	return d, nil
}

// getAppNonce returns a random application nonce (used for OTAA).
func getAppNonce() ([3]byte, error) {
	var b [3]byte
	if _, err := rand.Read(b[:]); err != nil {
		return b, err
	}
	return b, nil
}

// getNwkSKey returns the network session key.
func getNwkSKey(appkey lorawan.AES128Key, netID lorawan.NetID, appNonce [3]byte, devNonce [2]byte) (lorawan.AES128Key, error) {
	return getSKey(0x01, appkey, netID, appNonce, devNonce)
}

// getAppSKey returns the application session key.
func getAppSKey(appkey lorawan.AES128Key, netID lorawan.NetID, appNonce [3]byte, devNonce [2]byte) (lorawan.AES128Key, error) {
	return getSKey(0x02, appkey, netID, appNonce, devNonce)
}

func getSKey(typ byte, appkey lorawan.AES128Key, netID lorawan.NetID, appNonce [3]byte, devNonce [2]byte) (lorawan.AES128Key, error) {
	var key lorawan.AES128Key
	b := make([]byte, 0, 16)
	b = append(b, typ)

	// little endian
	for i := len(appNonce) - 1; i >= 0; i-- {
		b = append(b, appNonce[i])
	}
	for i := len(netID) - 1; i >= 0; i-- {
		b = append(b, netID[i])
	}
	for i := len(devNonce) - 1; i >= 0; i-- {
		b = append(b, devNonce[i])
	}
	pad := make([]byte, 7)
	b = append(b, pad...)

	block, err := aes.NewCipher(appkey[:])
	if err != nil {
		return key, err
	}
	if block.BlockSize() != len(b) {
		return key, fmt.Errorf("block-size of %d bytes is expected", len(b))
	}
	block.Encrypt(key[:], b)
	return key, nil
}

// NodeSessionAPI exports the NodeSession related functions.
type NodeSessionAPI struct {
	ctx Context
}

// NewNodeSessionAPI crestes a new NodeSessionAPI.
func NewNodeSessionAPI(ctx Context) *NodeSessionAPI {
	return &NodeSessionAPI{
		ctx: ctx,
	}
}

// Get returns the NodeSession for the given DevAddr.
func (a *NodeSessionAPI) Get(devAddr lorawan.DevAddr, ns *models.NodeSession) error {
	var err error
	*ns, err = getNodeSession(a.ctx.RedisPool, devAddr)
	return err
}

// GetByDevEUI returns the NodeSession for the given DevEUI.
func (a *NodeSessionAPI) GetByDevEUI(devEUI lorawan.EUI64, ns *models.NodeSession) error {
	var err error
	*ns, err = getNodeSessionByDevEUI(a.ctx.RedisPool, devEUI)
	return err
}

// Create creates the given NodeSession (activation by personalization).
// The DevAddr must contain the same NwkID as the configured NetID.
// Sessions will expire automatically after the configured TTL.
func (a *NodeSessionAPI) Create(ns models.NodeSession, devAddr *lorawan.DevAddr) error {
	// validate the NwkID
	if ns.DevAddr.NwkID() != a.ctx.NetID.NwkID() {
		return fmt.Errorf("DevAddr must contain NwkID %s", hex.EncodeToString([]byte{a.ctx.NetID.NwkID()}))
	}

	// validate that the node exists
	var node models.Node
	var err error
	if node, err = getNode(a.ctx.DB, ns.DevEUI); err != nil {
		return err
	}

	if ns.AppEUI != node.AppEUI {
		return fmt.Errorf("DevEUI %s belongs to AppEUI %s, got AppEUI %s", ns.AppEUI, node.AppEUI, ns.AppEUI)
	}

	if err = createNodeSession(a.ctx.RedisPool, ns); err != nil {
		return err
	}
	*devAddr = ns.DevAddr
	return nil
}

// Update updates the given NodeSession.
func (a *NodeSessionAPI) Update(ns models.NodeSession, devEUI *lorawan.EUI64) error {
	// validate the NwkID
	if ns.DevAddr.NwkID() != a.ctx.NetID.NwkID() {
		return fmt.Errorf("DevAddr must contain NwkID %s", hex.EncodeToString([]byte{a.ctx.NetID.NwkID()}))
	}

	if err := saveNodeSession(a.ctx.RedisPool, ns); err != nil {
		return err
	}
	*devEUI = ns.DevEUI
	return nil
}

// Delete the NodeSession matching the given DevAddr.
func (a *NodeSessionAPI) Delete(devAddr lorawan.DevAddr, deletedDevAddr *lorawan.DevAddr) error {
	if err := deleteNodeSession(a.ctx.RedisPool, devAddr); err != nil {
		return err
	}
	*deletedDevAddr = devAddr
	return nil
}

// GetRandomDevAddr returns a random DevAddr.
func (a *NodeSessionAPI) GetRandomDevAddr(dummy interface{}, devAddr *lorawan.DevAddr) error {
	var err error
	*devAddr, err = getRandomDevAddr(a.ctx.RedisPool, a.ctx.NetID)
	return err
}
<|endoftext|>


<commit_before>package watcher

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

	"github.com/datawire/teleproxy/internal/pkg/k8s"

	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"

	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

type empty struct{}

type listWatchAdapter struct {
	resource dynamic.NamespaceableResourceInterface
}

func (lw listWatchAdapter) List(options v1.ListOptions) (runtime.Object, error) {
	return lw.resource.List(options)
}

func (lw listWatchAdapter) Watch(options v1.ListOptions) (watch.Interface, error) {
	return lw.resource.Watch(options)
}

type Watcher struct {
	config       *rest.Config
	resources    []*v1.APIResourceList
	stores       map[string]cache.Store
	stop         chan empty
	stopChans    []chan struct{}
	stoppedChans []chan empty
}

// NewWatcher returns a Kubernetes Watcher for the specified cluster
func NewWatcher(kubeinfo *k8s.KubeInfo) *Watcher {
	config, err := kubeinfo.GetRestConfig()
	if err != nil {
		log.Fatalln("Failed to get REST config:", err)
	}

	disco, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	resources, err := disco.ServerResources()
	if err != nil {
		log.Fatal(err)
	}

	w := &Watcher{
		config:    config,
		resources: resources,
		stores:    make(map[string]cache.Store),
		stop:      make(chan empty),
	}

	go func() {
		<-w.stop
		for _, c := range w.stopChans {
			close(c)
		}
		for {
			<-w.stop
		}
	}()

	return w
}

func (w *Watcher) resolve(resource string) (string, string, v1.APIResource) {
	resource = strings.ToLower(resource)
	if resource == "" {
		return "", "", v1.APIResource{}
	}
	for _, rl := range w.resources {
		for _, r := range rl.APIResources {
			candidates := []string{
				r.Name,
				r.Kind,
				r.SingularName,
			}
			candidates = append(candidates, r.ShortNames...)

			for _, c := range candidates {
				if resource == strings.ToLower(c) {
					var group string
					var version string
					parts := strings.Split(rl.GroupVersion, "/")
					switch len(parts) {
					case 1:
						group = ""
						version = parts[0]
					case 2:
						group = parts[0]
						version = parts[1]
					default:
						panic("unrecognized GroupVersion")
					}
					return group, version, r
				}
			}
		}
	}
	return "", "", v1.APIResource{}
}

func (w *Watcher) Canonical(name string) string {
	parts := strings.Split(name, "/")

	var kind string
	switch len(parts) {
	case 1:
		kind = parts[0]
		name = ""
	case 2:
		kind = parts[0]
		name = parts[1]
	default:
		return ""
	}

	var namespace string
	parts = strings.Split(name, ".")
	switch len(parts) {
	case 0:
	case 1:
		namespace = "default"
	case 2:
		name = parts[0]
		namespace = parts[1]
	default:
		return ""
	}

	_, _, res := w.resolve(kind)
	kind = strings.ToLower(res.Kind)

	if name == "" {
		return kind
	}

	if res.Namespaced {
		return fmt.Sprintf("%s/%s.%s", kind, name, namespace)
	} else {
		switch namespace {
		case "":
			fallthrough
		case "default":
			return fmt.Sprintf("%s/%s", kind, name)
		default:
			return ""
		}
	}
}

func (w *Watcher) Watch(resources string, listener func(*Watcher)) error {
	group, version, res := w.resolve(resources)
	if version == "" {
		return fmt.Errorf("unknown resource: '%s'", resources)
	}

	dyn, err := dynamic.NewForConfig(w.config)
	if err != nil {
		log.Fatal(err)
	}

	resource := dyn.Resource(schema.GroupVersionResource{
		Group:    group,
		Version:  version,
		Resource: res.Name,
	})

	store, controller := cache.NewInformer(
		listWatchAdapter{resource},
		nil,
		5*time.Minute,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				listener(w)
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				listener(w)
			},
			DeleteFunc: func(obj interface{}) {
				listener(w)
			},
		},
	)

	w.stores[w.Canonical(res.Kind)] = store

	stopChan := make(chan struct{})
	stoppedChan := make(chan empty)
	w.stoppedChans = append(w.stoppedChans, stoppedChan)
	go func() {
		controller.Run(stopChan)
		close(stoppedChan)
	}()
	w.stopChans = append(w.stopChans, stopChan)

	return nil
}

func (w *Watcher) List(kind string) []Resource {
	kind = w.Canonical(kind)
	store, ok := w.stores[kind]
	if ok {
		objs := store.List()
		result := make([]Resource, len(objs))
		for idx, obj := range objs {
			result[idx] = obj.(*unstructured.Unstructured).UnstructuredContent()
		}
		return result
	} else {
		return nil
	}
}

func (w *Watcher) Get(kind, qname string) Resource {
	resources := w.List(kind)
	for _, res := range resources {
		if strings.ToLower(res.QName()) == strings.ToLower(qname) {
			return res
		}
	}
	return Resource{}
}

func (w *Watcher) Exists(kind, qname string) bool {
	return w.Get(kind, qname).Name() != ""
}

func (w *Watcher) Stop() {
	w.stop <- empty{}
}

func (w *Watcher) Wait() {
	for _, c := range w.stoppedChans {
		<-c
	}
}
<commit_msg>more fixes to watcher.Canonical<commit_after>package watcher

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

	"github.com/datawire/teleproxy/internal/pkg/k8s"

	"k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"

	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

type empty struct{}

type listWatchAdapter struct {
	resource dynamic.NamespaceableResourceInterface
}

func (lw listWatchAdapter) List(options v1.ListOptions) (runtime.Object, error) {
	return lw.resource.List(options)
}

func (lw listWatchAdapter) Watch(options v1.ListOptions) (watch.Interface, error) {
	return lw.resource.Watch(options)
}

type Watcher struct {
	config       *rest.Config
	resources    []*v1.APIResourceList
	stores       map[string]cache.Store
	stop         chan empty
	stopChans    []chan struct{}
	stoppedChans []chan empty
}

// NewWatcher returns a Kubernetes Watcher for the specified cluster
func NewWatcher(kubeinfo *k8s.KubeInfo) *Watcher {
	config, err := kubeinfo.GetRestConfig()
	if err != nil {
		log.Fatalln("Failed to get REST config:", err)
	}

	disco, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	resources, err := disco.ServerResources()
	if err != nil {
		log.Fatal(err)
	}

	w := &Watcher{
		config:    config,
		resources: resources,
		stores:    make(map[string]cache.Store),
		stop:      make(chan empty),
	}

	go func() {
		<-w.stop
		for _, c := range w.stopChans {
			close(c)
		}
		for {
			<-w.stop
		}
	}()

	return w
}

func (w *Watcher) resolve(resource string) (string, string, v1.APIResource) {
	resource = strings.ToLower(resource)
	if resource == "" {
		return "", "", v1.APIResource{}
	}
	for _, rl := range w.resources {
		for _, r := range rl.APIResources {
			candidates := []string{
				r.Name,
				r.Kind,
				r.SingularName,
			}
			candidates = append(candidates, r.ShortNames...)

			for _, c := range candidates {
				if resource == strings.ToLower(c) {
					var group string
					var version string
					parts := strings.Split(rl.GroupVersion, "/")
					switch len(parts) {
					case 1:
						group = ""
						version = parts[0]
					case 2:
						group = parts[0]
						version = parts[1]
					default:
						panic("unrecognized GroupVersion")
					}
					return group, version, r
				}
			}
		}
	}
	return "", "", v1.APIResource{}
}

func (w *Watcher) Canonical(name string) string {
	parts := strings.Split(name, "/")

	var kind string
	switch len(parts) {
	case 1:
		kind = parts[0]
		name = ""
	case 2:
		kind = parts[0]
		name = parts[1]
	default:
		return ""
	}

	_, _, res := w.resolve(kind)
	kind = strings.ToLower(res.Kind)

	if name == "" {
		return kind
	}

	if res.Namespaced {
		var namespace string

		parts = strings.Split(name, ".")
		switch len(parts) {
		case 1:
			namespace = "default"
		case 2:
			name = parts[0]
			namespace = parts[1]
		default:
			return ""
		}

		return fmt.Sprintf("%s/%s.%s", kind, name, namespace)
	} else {
		return fmt.Sprintf("%s/%s", kind, name)
	}
}

func (w *Watcher) Watch(resources string, listener func(*Watcher)) error {
	group, version, res := w.resolve(resources)
	if version == "" {
		return fmt.Errorf("unknown resource: '%s'", resources)
	}

	dyn, err := dynamic.NewForConfig(w.config)
	if err != nil {
		log.Fatal(err)
	}

	resource := dyn.Resource(schema.GroupVersionResource{
		Group:    group,
		Version:  version,
		Resource: res.Name,
	})

	store, controller := cache.NewInformer(
		listWatchAdapter{resource},
		nil,
		5*time.Minute,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				listener(w)
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				listener(w)
			},
			DeleteFunc: func(obj interface{}) {
				listener(w)
			},
		},
	)

	w.stores[w.Canonical(res.Kind)] = store

	stopChan := make(chan struct{})
	stoppedChan := make(chan empty)
	w.stoppedChans = append(w.stoppedChans, stoppedChan)
	go func() {
		controller.Run(stopChan)
		close(stoppedChan)
	}()
	w.stopChans = append(w.stopChans, stopChan)

	return nil
}

func (w *Watcher) List(kind string) []Resource {
	kind = w.Canonical(kind)
	store, ok := w.stores[kind]
	if ok {
		objs := store.List()
		result := make([]Resource, len(objs))
		for idx, obj := range objs {
			result[idx] = obj.(*unstructured.Unstructured).UnstructuredContent()
		}
		return result
	} else {
		return nil
	}
}

func (w *Watcher) Get(kind, qname string) Resource {
	resources := w.List(kind)
	for _, res := range resources {
		if strings.ToLower(res.QName()) == strings.ToLower(qname) {
			return res
		}
	}
	return Resource{}
}

func (w *Watcher) Exists(kind, qname string) bool {
	return w.Get(kind, qname).Name() != ""
}

func (w *Watcher) Stop() {
	w.stop <- empty{}
}

func (w *Watcher) Wait() {
	for _, c := range w.stoppedChans {
		<-c
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 packet

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"time"

	"github.com/coreos/coreos-metadata/internal/providers"
	"github.com/coreos/coreos-metadata/internal/retry"
	"github.com/packethost/packngo/metadata"
)

func FetchMetadata() (providers.Metadata, error) {
	body, err := retry.Client{
		InitialBackoff: time.Second,
		MaxBackoff:     time.Second * 5,
		MaxAttempts:    10,
	}.Get(metadata.BaseURL + "/metadata")
	if err != nil {
		return providers.Metadata{}, err
	}

	var data struct {
		Error string `json:"error"`
		*metadata.CurrentDevice
	}

	if err := json.Unmarshal(body, &data); err != nil {
		return providers.Metadata{}, err
	}

	if data.Error != "" {
		return providers.Metadata{}, errors.New(data.Error)
	}

	attrs := ipAddresses(data.Network)
	attrs["PACKET_HOSTNAME"] = data.Hostname

	return providers.Metadata{
		Attributes: attrs,
		Hostname:   data.Hostname,
		SshKeys:    data.SSHKeys,
	}, nil
}

func ipAddresses(network metadata.NetworkInfo) map[string]string {
	var publicIPv4, privateIPv4, publicIPv6, privateIPv6 []net.IP

	for _, addr := range network.Addresses {
		switch {
		case addr.Family == 4 && addr.Public:
			publicIPv4 = append(publicIPv4, addr.Address)

		case addr.Family == 4 && !addr.Public:
			privateIPv4 = append(privateIPv4, addr.Address)

		case addr.Family == 6 && addr.Public:
			publicIPv6 = append(publicIPv6, addr.Address)

		case addr.Family == 6 && !addr.Public:
			privateIPv6 = append(privateIPv6, addr.Address)
		}
	}

	addresses := make(map[string]string)

	for i, ip := range publicIPv4 {
		addresses[fmt.Sprintf("PACKET_IPV4_PUBLIC_%d", i)] = providers.String(ip)
	}

	for i, ip := range privateIPv4 {
		addresses[fmt.Sprintf("PACKET_IPV4_PRIVATE_%d", i)] = providers.String(ip)
	}

	for i, ip := range publicIPv6 {
		addresses[fmt.Sprintf("PACKET_IPV6_PUBLIC_%d", i)] = providers.String(ip)
	}

	for i, ip := range privateIPv6 {
		addresses[fmt.Sprintf("PACKET_IPV6_PRIVATE_%d", i)] = providers.String(ip)
	}

	return addresses
}
<commit_msg>providers/packet: expose phone home url<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 packet

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"time"

	"github.com/coreos/coreos-metadata/internal/providers"
	"github.com/coreos/coreos-metadata/internal/retry"
	"github.com/packethost/packngo/metadata"
)

func FetchMetadata() (providers.Metadata, error) {
	body, err := retry.Client{
		InitialBackoff: time.Second,
		MaxBackoff:     time.Second * 5,
		MaxAttempts:    10,
	}.Get(metadata.BaseURL + "/metadata")
	if err != nil {
		return providers.Metadata{}, err
	}

	var data struct {
		Error        string `json:"error"`
		PhoneHomeURL string `json:"phone_home_url"`
		*metadata.CurrentDevice
	}

	if err := json.Unmarshal(body, &data); err != nil {
		return providers.Metadata{}, err
	}

	if data.Error != "" {
		return providers.Metadata{}, errors.New(data.Error)
	}

	attrs := ipAddresses(data.Network)
	attrs["PACKET_HOSTNAME"] = data.Hostname
	attrs["PACKET_PHONE_HOME_URL"] = data.PhoneHomeURL

	return providers.Metadata{
		Attributes: attrs,
		Hostname:   data.Hostname,
		SshKeys:    data.SSHKeys,
	}, nil
}

func ipAddresses(network metadata.NetworkInfo) map[string]string {
	var publicIPv4, privateIPv4, publicIPv6, privateIPv6 []net.IP

	for _, addr := range network.Addresses {
		switch {
		case addr.Family == 4 && addr.Public:
			publicIPv4 = append(publicIPv4, addr.Address)

		case addr.Family == 4 && !addr.Public:
			privateIPv4 = append(privateIPv4, addr.Address)

		case addr.Family == 6 && addr.Public:
			publicIPv6 = append(publicIPv6, addr.Address)

		case addr.Family == 6 && !addr.Public:
			privateIPv6 = append(privateIPv6, addr.Address)
		}
	}

	addresses := make(map[string]string)

	for i, ip := range publicIPv4 {
		addresses[fmt.Sprintf("PACKET_IPV4_PUBLIC_%d", i)] = providers.String(ip)
	}

	for i, ip := range privateIPv4 {
		addresses[fmt.Sprintf("PACKET_IPV4_PRIVATE_%d", i)] = providers.String(ip)
	}

	for i, ip := range publicIPv6 {
		addresses[fmt.Sprintf("PACKET_IPV6_PUBLIC_%d", i)] = providers.String(ip)
	}

	for i, ip := range privateIPv6 {
		addresses[fmt.Sprintf("PACKET_IPV6_PRIVATE_%d", i)] = providers.String(ip)
	}

	return addresses
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"net/http"
	"net/http/httptest"
	"testing"
)

// Returns an *http.Client which will receive a response containing
// body, headers, and status code specified. Must close received server.
// Proxies all requests made using returned client to a httptest.Server
// which uses an http.Handler to handle requests.
// See: http://keighl.com/post/mocking-http-responses-in-golang/
func MockClient(code int, body []byte, headers map[string]string) (*Client, *httptest.Server) {
	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		for k, v := range headers {
			w.Header().Set(k, v)
		}
		w.WriteHeader(code)
		w.Write(body)
	}))
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return &Client{URL: server.URL, HTTPClient: &http.Client{Transport: transport}}, server
}

// Same as MockClient but the server uses the defined http.Handler
func MockClientHandler(fn func(http.ResponseWriter, *http.Request)) (*Client, *httptest.Server) {
	server := httptest.NewTLSServer(http.HandlerFunc(fn))
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return &Client{URL: server.URL, HTTPClient: &http.Client{Transport: transport}}, server
}

// Calls t.Error with err, and writes status code to response
func ErrorWithCode(t *testing.T, w http.ResponseWriter, err string, code int) {
	w.WriteHeader(code)
	t.Error(err)
}
<commit_msg>correct docs<commit_after>package main

import (
	"crypto/tls"
	"net/http"
	"net/http/httptest"
	"testing"
)

// Returns an *Client which will receive a response containing
// body, headers, and status code specified. Must close received server.
// See: http://keighl.com/post/mocking-http-responses-in-golang/
func MockClient(code int, body []byte, headers map[string]string) (*Client, *httptest.Server) {
	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		for k, v := range headers {
			w.Header().Set(k, v)
		}
		w.WriteHeader(code)
		w.Write(body)
	}))
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return &Client{URL: server.URL, HTTPClient: &http.Client{Transport: transport}}, server
}

// Same as MockClient but the server uses the defined http.Handler
func MockClientHandler(fn func(http.ResponseWriter, *http.Request)) (*Client, *httptest.Server) {
	server := httptest.NewTLSServer(http.HandlerFunc(fn))
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return &Client{URL: server.URL, HTTPClient: &http.Client{Transport: transport}}, server
}

// Calls t.Error with err, and writes status code to response
func ErrorWithCode(t *testing.T, w http.ResponseWriter, err string, code int) {
	w.WriteHeader(code)
	t.Error(err)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	"os"
	"reflect"
	"strconv"
	"time"

	. "github.com/onsi/ginkgo"
)

const (
	FederatedServiceTimeout = 60 * time.Second

	FederatedServiceName    = "federated-service"
	FederatedServicePodName = "federated-service-test-pod"
)

var FederatedServiceLabels = map[string]string{
	"foo": "bar",
}

var _ = framework.KubeDescribe("[Feature:Federation]", func() {
	f := framework.NewDefaultFederatedFramework("federated-service")
	var clusters map[string]*cluster // All clusters, keyed by cluster name
	var federationName string
	var primaryClusterName string // The name of the "primary" cluster

	var _ = Describe("Federated Services", func() {
		BeforeEach(func() {
			framework.SkipUnlessFederated(f.Client)

			// TODO: Federation API server should be able to answer this.
			if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
				federationName = DefaultFederationName
			}

			clusters = map[string]*cluster{}
			primaryClusterName = registerClusters(clusters, UserAgentName, federationName, f)
		})

		AfterEach(func() {
			unregisterClusters(clusters, f)
		})

		Describe("Service creation", func() {
			BeforeEach(func() {
				framework.SkipUnlessFederated(f.Client)
				// Placeholder
			})

			AfterEach(func() {
				framework.SkipUnlessFederated(f.Client)
				// Placeholder
			})

			It("should succeed", func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				service := createServiceOrFail(f.FederationClientset_1_4, nsName, FederatedServiceName)
				By(fmt.Sprintf("Creation of service %q in namespace %q succeeded.  Deleting service.", service.Name, nsName))

				// Cleanup
				err := f.FederationClientset_1_4.Services(nsName).Delete(service.Name, &api.DeleteOptions{})
				framework.ExpectNoError(err, "Error deleting service %q in namespace %q", service.Name, service.Namespace)
				By(fmt.Sprintf("Deletion of service %q in namespace %q succeeded.", service.Name, nsName))

				By(fmt.Sprintf("Deleting service shards and their provider resources in underlying clusters for service %q in namespace %q", service.Name, nsName))
				cleanupServiceShardsAndProviderResources(nsName, service, clusters)
			})

			It("should create matching services in underlying clusters", func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				service := createServiceOrFail(f.FederationClientset_1_4, nsName, FederatedServiceName)
				defer func() { // Cleanup
					By(fmt.Sprintf("Deleting service %q in namespace %q", service.Name, nsName))
					err := f.FederationClientset_1_4.Services(nsName).Delete(service.Name, &api.DeleteOptions{})
					framework.ExpectNoError(err, "Error deleting service %q in namespace %q", service.Name, nsName)

					By(fmt.Sprintf("Deleting service shards and their provider resources in underlying clusters for service %q in namespace %q", service.Name, nsName))
					cleanupServiceShardsAndProviderResources(nsName, service, clusters)
				}()
				waitForServiceShardsOrFail(nsName, service, clusters)
			})
		})

		var _ = Describe("DNS", func() {

			var (
				service *v1.Service
			)

			BeforeEach(func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				createBackendPodsOrFail(clusters, nsName, FederatedServicePodName)
				service = createServiceOrFail(f.FederationClientset_1_4, nsName, FederatedServiceName)
				waitForServiceShardsOrFail(nsName, service, clusters)
			})

			AfterEach(func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				deleteBackendPodsOrFail(clusters, nsName)

				if service != nil {
					deleteServiceOrFail(f.FederationClientset_1_4, nsName, service.Name)
					service = nil

					By(fmt.Sprintf("Deleting service shards and their provider resources in underlying clusters for service %q in namespace %q", service.Name, nsName))
					cleanupServiceShardsAndProviderResources(nsName, service, clusters)
				} else {
					By("No service to delete.  Service is nil")
				}
			})

			It("should be able to discover a federated service", func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				svcDNSNames := []string{
					FederatedServiceName,
					fmt.Sprintf("%s.%s", FederatedServiceName, nsName),
					fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, nsName),
					fmt.Sprintf("%s.%s.%s", FederatedServiceName, nsName, federationName),
					fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, nsName, federationName),
				}
				// TODO(mml): This could be much faster.  We can launch all the test
				// pods, perhaps in the BeforeEach, and then just poll until we get
				// successes/failures from them all.
				for i, DNSName := range svcDNSNames {
					discoverService(f, DNSName, true, "federated-service-e2e-discovery-pod-"+strconv.Itoa(i))
				}
			})

			Context("non-local federated service", func() {
				BeforeEach(func() {
					framework.SkipUnlessFederated(f.Client)

					// Delete all the backend pods from the shard which is local to the discovery pod.
					deleteOneBackendPodOrFail(clusters[primaryClusterName])

				})

				It("should be able to discover a non-local federated service", func() {
					framework.SkipUnlessFederated(f.Client)

					nsName := f.FederationNamespace.Name
					svcDNSNames := []string{
						fmt.Sprintf("%s.%s.%s", FederatedServiceName, nsName, federationName),
						fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, nsName, federationName),
					}
					for i, name := range svcDNSNames {
						discoverService(f, name, true, "federated-service-e2e-discovery-pod-"+strconv.Itoa(i))
					}
				})

				// TODO(mml): This currently takes 9 minutes.  Consider reducing the
				// TTL and/or running the pods in parallel.
				Context("[Slow] missing local service", func() {
					It("should never find DNS entries for a missing local service", func() {
						framework.SkipUnlessFederated(f.Client)

						nsName := f.FederationNamespace.Name
						localSvcDNSNames := []string{
							FederatedServiceName,
							fmt.Sprintf("%s.%s", FederatedServiceName, nsName),
							fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, nsName),
						}
						for i, name := range localSvcDNSNames {
							discoverService(f, name, false, "federated-service-e2e-discovery-pod-"+strconv.Itoa(i))
						}
					})
				})
			})
		})
	})
})

/*
   equivalent returns true if the two services are equivalent.  Fields which are expected to differ between
   federated services and the underlying cluster services (e.g. ClusterIP, LoadBalancerIP etc) are ignored.
*/
func equivalent(federationService, clusterService v1.Service) bool {
	// TODO: I think that we need a DeepCopy here to avoid clobbering our parameters.
	clusterService.Spec.ClusterIP = federationService.Spec.ClusterIP
	clusterService.Spec.ExternalIPs = federationService.Spec.ExternalIPs
	clusterService.Spec.DeprecatedPublicIPs = federationService.Spec.DeprecatedPublicIPs
	clusterService.Spec.LoadBalancerIP = federationService.Spec.LoadBalancerIP
	clusterService.Spec.LoadBalancerSourceRanges = federationService.Spec.LoadBalancerSourceRanges
	// N.B. We cannot iterate over the port objects directly, as their values
	// only get copied and our updates will get lost.
	for i := range clusterService.Spec.Ports {
		clusterService.Spec.Ports[i].NodePort = federationService.Spec.Ports[i].NodePort
	}
	return reflect.DeepEqual(clusterService.Spec, federationService.Spec)
}
<commit_msg>Address review comments and minor panic fixes.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	"os"
	"reflect"
	"strconv"
	"time"

	. "github.com/onsi/ginkgo"
)

const (
	FederatedServiceTimeout = 60 * time.Second

	FederatedServiceName    = "federated-service"
	FederatedServicePodName = "federated-service-test-pod"
)

var FederatedServiceLabels = map[string]string{
	"foo": "bar",
}

var _ = framework.KubeDescribe("[Feature:Federation]", func() {
	f := framework.NewDefaultFederatedFramework("federated-service")
	var clusters map[string]*cluster // All clusters, keyed by cluster name
	var federationName string
	var primaryClusterName string // The name of the "primary" cluster

	var _ = Describe("Federated Services", func() {
		BeforeEach(func() {
			framework.SkipUnlessFederated(f.Client)

			// TODO: Federation API server should be able to answer this.
			if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
				federationName = DefaultFederationName
			}

			clusters = map[string]*cluster{}
			primaryClusterName = registerClusters(clusters, UserAgentName, federationName, f)
		})

		AfterEach(func() {
			unregisterClusters(clusters, f)
		})

		Describe("Service creation", func() {
			var (
				service *v1.Service
				nsName  string
			)

			BeforeEach(func() {
				framework.SkipUnlessFederated(f.Client)
				// Placeholder
			})

			AfterEach(func() {
				framework.SkipUnlessFederated(f.Client)

				if service != nil {
					By(fmt.Sprintf("Deleting service shards and their provider resources in underlying clusters for service %q in namespace %q", service.Name, nsName))
					cleanupServiceShardsAndProviderResources(nsName, service, clusters)
					service = nil
					nsName = ""
				}
			})

			It("should succeed", func() {
				framework.SkipUnlessFederated(f.Client)

				nsName = f.FederationNamespace.Name
				service = createServiceOrFail(f.FederationClientset_1_4, nsName, FederatedServiceName)
				By(fmt.Sprintf("Creation of service %q in namespace %q succeeded.  Deleting service.", service.Name, nsName))

				// Cleanup
				err := f.FederationClientset_1_4.Services(nsName).Delete(service.Name, &api.DeleteOptions{})
				framework.ExpectNoError(err, "Error deleting service %q in namespace %q", service.Name, service.Namespace)
				By(fmt.Sprintf("Deletion of service %q in namespace %q succeeded.", service.Name, nsName))
			})

			It("should create matching services in underlying clusters", func() {
				framework.SkipUnlessFederated(f.Client)

				nsName = f.FederationNamespace.Name
				service = createServiceOrFail(f.FederationClientset_1_4, nsName, FederatedServiceName)
				defer func() { // Cleanup
					By(fmt.Sprintf("Deleting service %q in namespace %q", service.Name, nsName))
					err := f.FederationClientset_1_4.Services(nsName).Delete(service.Name, &api.DeleteOptions{})
					framework.ExpectNoError(err, "Error deleting service %q in namespace %q", service.Name, nsName)
				}()
				waitForServiceShardsOrFail(nsName, service, clusters)
			})
		})

		var _ = Describe("DNS", func() {

			var (
				service *v1.Service
			)

			BeforeEach(func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				createBackendPodsOrFail(clusters, nsName, FederatedServicePodName)
				service = createServiceOrFail(f.FederationClientset_1_4, nsName, FederatedServiceName)
				waitForServiceShardsOrFail(nsName, service, clusters)
			})

			AfterEach(func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				deleteBackendPodsOrFail(clusters, nsName)

				if service != nil {
					deleteServiceOrFail(f.FederationClientset_1_4, nsName, service.Name)

					By(fmt.Sprintf("Deleting service shards and their provider resources in underlying clusters for service %q in namespace %q", service.Name, nsName))
					cleanupServiceShardsAndProviderResources(nsName, service, clusters)

					service = nil
				} else {
					By("No service to delete.  Service is nil")
				}
			})

			It("should be able to discover a federated service", func() {
				framework.SkipUnlessFederated(f.Client)

				nsName := f.FederationNamespace.Name
				svcDNSNames := []string{
					FederatedServiceName,
					fmt.Sprintf("%s.%s", FederatedServiceName, nsName),
					fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, nsName),
					fmt.Sprintf("%s.%s.%s", FederatedServiceName, nsName, federationName),
					fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, nsName, federationName),
				}
				// TODO(mml): This could be much faster.  We can launch all the test
				// pods, perhaps in the BeforeEach, and then just poll until we get
				// successes/failures from them all.
				for i, DNSName := range svcDNSNames {
					discoverService(f, DNSName, true, "federated-service-e2e-discovery-pod-"+strconv.Itoa(i))
				}
			})

			Context("non-local federated service", func() {
				BeforeEach(func() {
					framework.SkipUnlessFederated(f.Client)

					// Delete all the backend pods from the shard which is local to the discovery pod.
					deleteOneBackendPodOrFail(clusters[primaryClusterName])

				})

				It("should be able to discover a non-local federated service", func() {
					framework.SkipUnlessFederated(f.Client)

					nsName := f.FederationNamespace.Name
					svcDNSNames := []string{
						fmt.Sprintf("%s.%s.%s", FederatedServiceName, nsName, federationName),
						fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, nsName, federationName),
					}
					for i, name := range svcDNSNames {
						discoverService(f, name, true, "federated-service-e2e-discovery-pod-"+strconv.Itoa(i))
					}
				})

				// TODO(mml): This currently takes 9 minutes.  Consider reducing the
				// TTL and/or running the pods in parallel.
				Context("[Slow] missing local service", func() {
					It("should never find DNS entries for a missing local service", func() {
						framework.SkipUnlessFederated(f.Client)

						nsName := f.FederationNamespace.Name
						localSvcDNSNames := []string{
							FederatedServiceName,
							fmt.Sprintf("%s.%s", FederatedServiceName, nsName),
							fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, nsName),
						}
						for i, name := range localSvcDNSNames {
							discoverService(f, name, false, "federated-service-e2e-discovery-pod-"+strconv.Itoa(i))
						}
					})
				})
			})
		})
	})
})

/*
   equivalent returns true if the two services are equivalent.  Fields which are expected to differ between
   federated services and the underlying cluster services (e.g. ClusterIP, LoadBalancerIP etc) are ignored.
*/
func equivalent(federationService, clusterService v1.Service) bool {
	// TODO: I think that we need a DeepCopy here to avoid clobbering our parameters.
	clusterService.Spec.ClusterIP = federationService.Spec.ClusterIP
	clusterService.Spec.ExternalIPs = federationService.Spec.ExternalIPs
	clusterService.Spec.DeprecatedPublicIPs = federationService.Spec.DeprecatedPublicIPs
	clusterService.Spec.LoadBalancerIP = federationService.Spec.LoadBalancerIP
	clusterService.Spec.LoadBalancerSourceRanges = federationService.Spec.LoadBalancerSourceRanges
	// N.B. We cannot iterate over the port objects directly, as their values
	// only get copied and our updates will get lost.
	for i := range clusterService.Spec.Ports {
		clusterService.Spec.Ports[i].NodePort = federationService.Spec.Ports[i].NodePort
	}
	return reflect.DeepEqual(clusterService.Spec, federationService.Spec)
}
<|endoftext|>


<commit_before>package marionette_client

import (
	"testing"
	"time"
)

func TestWait(t *testing.T) {
	client.SetContext(Context(CONTENT))
	client.Navigate("http://www.w3schools.com/ajax/tryit.asp?filename=tryajax_get")

	timeout := time.Duration(10) * time.Second
	condition := ElementIsPresent(By(ID), "stackH")
	ok, v, err := Wait(client).For(timeout).Until(condition)

	if err != nil || !ok {
		t.Fatalf("%#v", err)
	}

	v.Click()

	err = client.SwitchToFrame(By(ID), "iframeResult")
	if err != nil {
		t.Fatalf("%#v", err)
	}

	e, err := client.FindElement(By(TAG_NAME), "button")
	if err != nil {
		t.Fatal("%#v", err)
	}

	e.Click()
}

func TestNotPresent(t *testing.T) {
	client.SwitchToParentFrame()
	client.ActiveFrame()

	timeout := time.Duration(10) * time.Second
	condition := ElementIsNotPresent(By(ID), "non-existing-element")
	ok, _, _ := Wait(client).For(timeout).Until(condition)

	if !ok {
		t.Fatal("Element Was Found in ElementIsNotPresent test.")
	}
}

// working - if called before other tests all hell will break loose
func TestCloseWindow(t *testing.T) {
	r, err := client.CloseWindow()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

// working
//func TestQuitApplication(t *testing.T) {
//	r, err := client.QuitApplication()
//	if err != nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log(r.Value)
//}
<commit_msg>added AcceptDialog(), DismissDialog() tests<commit_after>package marionette_client

import (
	"testing"
	"time"
)

func TestWait(t *testing.T) {
	client.SetContext(Context(CONTENT))
	client.Navigate("http://www.w3schools.com/ajax/tryit.asp?filename=tryajax_get")

	timeout := time.Duration(10) * time.Second
	condition := ElementIsPresent(By(ID), "stackH")
	ok, v, err := Wait(client).For(timeout).Until(condition)

	if err != nil || !ok {
		t.Fatalf("%#v", err)
	}

	v.Click()

	err = client.SwitchToFrame(By(ID), "iframeResult")
	if err != nil {
		t.Fatalf("%#v", err)
	}

	e, err := client.FindElement(By(TAG_NAME), "button")
	if err != nil {
		t.Fatal("%#v", err)
	}

	e.Click()
}

func TestAlert(t *testing.T) {
	client.Get("http://www.abola.pt")
	var text string = "marionette is cool or what?"
	var script string = "alert('" + text + "');"
	args := []interface{}{}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	err = client.AcceptDialog()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	r, err = client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	err = client.AcceptDialog()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestNotPresent(t *testing.T) {
	client.SwitchToParentFrame()
	client.ActiveFrame()

	timeout := time.Duration(10) * time.Second
	condition := ElementIsNotPresent(By(ID), "non-existing-element")
	ok, _, _ := Wait(client).For(timeout).Until(condition)

	if !ok {
		t.Fatal("Element Was Found in ElementIsNotPresent test.")
	}
}

// working - if called before other tests all hell will break loose
func TestCloseWindow(t *testing.T) {
	r, err := client.CloseWindow()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

// working
//func TestQuitApplication(t *testing.T) {
//	r, err := client.QuitApplication()
//	if err != nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log(r.Value)
//}
<|endoftext|>