<commit_before>/*
Copyright 2020 The cert-manager 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 certificate

import (
	"context"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
	v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	"github.com/jetstack/cert-manager/test/e2e/framework/addon"
	"github.com/jetstack/cert-manager/test/e2e/framework/helper/featureset"
	"github.com/jetstack/cert-manager/test/e2e/framework/helper/validation"
	"github.com/jetstack/cert-manager/test/e2e/suite/issuers/acme/dnsproviders"
	"github.com/jetstack/cert-manager/test/e2e/util"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

type dns01Provider interface {
	Details() *dnsproviders.Details
	addon.Addon
}

var _ = framework.CertManagerDescribe("ACME Certificate (DNS01)", func() {
	// TODO: add better logic to handle other DNS providers
	testRFC2136DNSProvider()
})

func testRFC2136DNSProvider() bool {
	name := "rfc2136"
	return Context("With "+name+" credentials configured", func() {
		f := framework.NewDefaultFramework("create-acme-certificate-dns01-" + name)

		issuerName := "test-acme-issuer"
		certificateName := "test-acme-certificate"
		certificateSecretName := "test-acme-certificate"

		p := &dnsproviders.RFC2136{}
		f.RequireAddon(p)

		dnsDomain := ""

		// ACME Issuer does not return a ca.crt. See:
		// https://github.com/jetstack/cert-manager/issues/1571
		unsupportedFeatures := featureset.NewFeatureSet(featureset.SaveCAToSecret)
		validations := validation.CertificateSetForUnsupportedFeatureSet(unsupportedFeatures)

		BeforeEach(func() {
			By("Creating an Issuer")
			dnsDomain = util.RandomSubdomain(p.Details().BaseDomain)
			issuer := gen.Issuer(issuerName,
				gen.SetIssuerACME(cmacme.ACMEIssuer{
					SkipTLSVerify: true,
					Server:        f.Config.Addons.ACMEServer.URL,
					Email:         f.Config.Addons.ACMEServer.TestingACMEEmail,
					PrivateKey: cmmeta.SecretKeySelector{
						LocalObjectReference: cmmeta.LocalObjectReference{
							Name: f.Config.Addons.ACMEServer.TestingACMEPrivateKey,
						},
					},
					Solvers: []cmacme.ACMEChallengeSolver{
						{
							DNS01: &p.Details().ProviderConfig,
						},
					},
				}))
			issuer.Namespace = f.Namespace.Name
			issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{})
			Expect(err).NotTo(HaveOccurred())
			By("Waiting for Issuer to become Ready")
			err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
				issuerName,
				v1.IssuerCondition{
					Type:   v1.IssuerConditionReady,
					Status: cmmeta.ConditionTrue,
				})
			Expect(err).NotTo(HaveOccurred())
			By("Verifying the ACME account URI is set")
			err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
				issuerName,
				func(i *v1.Issuer) (bool, error) {
					if i.GetStatus().ACMEStatus().URI == "" {
						return false, nil
					}
					return true, nil
				})
			Expect(err).NotTo(HaveOccurred())
			By("Verifying ACME account private key exists")
			secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), f.Config.Addons.ACMEServer.TestingACMEPrivateKey, metav1.GetOptions{})
			Expect(err).NotTo(HaveOccurred())
			if len(secret.Data) != 1 {
				Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
			}
		})

		AfterEach(func() {
			By("Cleaning up")
			f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.TODO(), issuerName, metav1.DeleteOptions{})
			f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), f.Config.Addons.ACMEServer.TestingACMEPrivateKey, metav1.DeleteOptions{})
			f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), certificateSecretName, metav1.DeleteOptions{})
		})

		It("should obtain a signed certificate for a regular domain", func() {
			By("Creating a Certificate")

			certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)

			cert := gen.Certificate(certificateName,
				gen.SetCertificateSecretName(certificateSecretName),
				gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
				gen.SetCertificateDNSNames(dnsDomain),
			)
			cert.Namespace = f.Namespace.Name

			cert, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
			Expect(err).NotTo(HaveOccurred())

			By("Waiting for the Certificate to be issued...")
			_, err = f.Helper().WaitForCertificateReady(f.Namespace.Name, certificateName, time.Minute*5)
			Expect(err).NotTo(HaveOccurred())

			By("Validating the issued Certificate...")
			err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
			Expect(err).NotTo(HaveOccurred())
		})

		It("should obtain a signed certificate for a wildcard domain", func() {
			By("Creating a Certificate")

			cert := gen.Certificate(certificateName,
				gen.SetCertificateSecretName(certificateSecretName),
				gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
				gen.SetCertificateDNSNames("*."+dnsDomain),
			)
			cert.Namespace = f.Namespace.Name

			cert, err := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name).Create(context.TODO(), cert, metav1.CreateOptions{})
			Expect(err).NotTo(HaveOccurred())

			By("Waiting for the Certificate to be issued...")
			_, err = f.Helper().WaitForCertificateReady(f.Namespace.Name, certificateName, time.Minute*5)
			Expect(err).NotTo(HaveOccurred())

			By("Validating the issued Certificate...")
			err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
			Expect(err).NotTo(HaveOccurred())
		})

		It("should obtain a signed certificate for a wildcard and apex domain", func() {
			By("Creating a Certificate")

			cert := gen.Certificate(certificateName,
				gen.SetCertificateSecretName(certificateSecretName),
				gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
				gen.SetCertificateDNSNames("*."+dnsDomain, dnsDomain),
			)
			cert.Namespace = f.Namespace.Name

			cert, err := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name).Create(context.TODO(), cert, metav1.CreateOptions{})
			Expect(err).NotTo(HaveOccurred())
			// use a longer timeout for this, as it requires performing 2 dns validations in serial
			By("Waiting for the Certificate to be issued...")
			_, err = f.Helper().WaitForCertificateReady(f.Namespace.Name, certificateName, time.Minute*10)
			Expect(err).NotTo(HaveOccurred())

			By("Validating the issued Certificate...")
			err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
			Expect(err).NotTo(HaveOccurred())
		})
	})
}
<commit_msg>Remove issuers specific tests that are already included in the conformance tests: remove dns01: "should obtain a signed certificate for a regular domain" remove dns01: "should obtain a signed certificate for a wildcard domain"<commit_after>/*
Copyright 2020 The cert-manager 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 certificate

import (
	"context"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
	v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	"github.com/jetstack/cert-manager/test/e2e/framework/addon"
	"github.com/jetstack/cert-manager/test/e2e/framework/helper/featureset"
	"github.com/jetstack/cert-manager/test/e2e/framework/helper/validation"
	"github.com/jetstack/cert-manager/test/e2e/suite/issuers/acme/dnsproviders"
	"github.com/jetstack/cert-manager/test/e2e/util"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

type dns01Provider interface {
	Details() *dnsproviders.Details
	addon.Addon
}

var _ = framework.CertManagerDescribe("ACME Certificate (DNS01)", func() {
	// TODO: add better logic to handle other DNS providers
	testRFC2136DNSProvider()
})

func testRFC2136DNSProvider() bool {
	name := "rfc2136"
	return Context("With "+name+" credentials configured", func() {
		f := framework.NewDefaultFramework("create-acme-certificate-dns01-" + name)

		issuerName := "test-acme-issuer"
		certificateName := "test-acme-certificate"
		certificateSecretName := "test-acme-certificate"

		p := &dnsproviders.RFC2136{}
		f.RequireAddon(p)

		dnsDomain := ""

		// ACME Issuer does not return a ca.crt. See:
		// https://github.com/jetstack/cert-manager/issues/1571
		unsupportedFeatures := featureset.NewFeatureSet(featureset.SaveCAToSecret)
		validations := validation.CertificateSetForUnsupportedFeatureSet(unsupportedFeatures)

		BeforeEach(func() {
			By("Creating an Issuer")
			dnsDomain = util.RandomSubdomain(p.Details().BaseDomain)
			issuer := gen.Issuer(issuerName,
				gen.SetIssuerACME(cmacme.ACMEIssuer{
					SkipTLSVerify: true,
					Server:        f.Config.Addons.ACMEServer.URL,
					Email:         f.Config.Addons.ACMEServer.TestingACMEEmail,
					PrivateKey: cmmeta.SecretKeySelector{
						LocalObjectReference: cmmeta.LocalObjectReference{
							Name: f.Config.Addons.ACMEServer.TestingACMEPrivateKey,
						},
					},
					Solvers: []cmacme.ACMEChallengeSolver{
						{
							DNS01: &p.Details().ProviderConfig,
						},
					},
				}))
			issuer.Namespace = f.Namespace.Name
			issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{})
			Expect(err).NotTo(HaveOccurred())
			By("Waiting for Issuer to become Ready")
			err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
				issuerName,
				v1.IssuerCondition{
					Type:   v1.IssuerConditionReady,
					Status: cmmeta.ConditionTrue,
				})
			Expect(err).NotTo(HaveOccurred())
			By("Verifying the ACME account URI is set")
			err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
				issuerName,
				func(i *v1.Issuer) (bool, error) {
					if i.GetStatus().ACMEStatus().URI == "" {
						return false, nil
					}
					return true, nil
				})
			Expect(err).NotTo(HaveOccurred())
			By("Verifying ACME account private key exists")
			secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), f.Config.Addons.ACMEServer.TestingACMEPrivateKey, metav1.GetOptions{})
			Expect(err).NotTo(HaveOccurred())
			if len(secret.Data) != 1 {
				Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
			}
		})

		AfterEach(func() {
			By("Cleaning up")
			f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.TODO(), issuerName, metav1.DeleteOptions{})
			f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), f.Config.Addons.ACMEServer.TestingACMEPrivateKey, metav1.DeleteOptions{})
			f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), certificateSecretName, metav1.DeleteOptions{})
		})

		It("should obtain a signed certificate for a wildcard and apex domain", func() {
			By("Creating a Certificate")

			cert := gen.Certificate(certificateName,
				gen.SetCertificateSecretName(certificateSecretName),
				gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
				gen.SetCertificateDNSNames("*."+dnsDomain, dnsDomain),
			)
			cert.Namespace = f.Namespace.Name

			cert, err := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name).Create(context.TODO(), cert, metav1.CreateOptions{})
			Expect(err).NotTo(HaveOccurred())
			// use a longer timeout for this, as it requires performing 2 dns validations in serial
			By("Waiting for the Certificate to be issued...")
			_, err = f.Helper().WaitForCertificateReady(f.Namespace.Name, certificateName, time.Minute*10)
			Expect(err).NotTo(HaveOccurred())

			By("Validating the issued Certificate...")
			err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
			Expect(err).NotTo(HaveOccurred())
		})
	})
}
<|endoftext|>


<commit_before>package main

import "fmt"

type BinOp func(x, y int) int

func main() {
	x, y := 2, 3
	f := CreateBinOp("+")(x, y)
	fmt.Printf("%d + %d = %d\n", x, y, f)

	g := intSeq()
	// everytime we call the function value g we get next int value
	for i := 0; i < 10; i++ {
		fmt.Printf("%v ", g())
	}
}

// notice the type of return value of the function CreateBinOp
// is the same as the type of the BinOp, hence we can replace this with BinOp.
// Hence line 23 is equivalent to line 22
//func CreateBinOp(op string) func(x, y int) int {
func CreateBinOp(op string) BinOp {
	var r BinOp // notice the type of r

	switch op {
	case "+":
		r = func(x, y int) int { return x + y }
	case "-":
		r = func(x, y int) int { return x - y }
	case "*":
		r = func(x, y int) int { return x * y }
	case "/":
		r = func(x, y int) int { return x / y }
	}

	return r
}

// Function literals are closures: they may refer to variables defined in a
// surrounding function. Those variables are then shared between the surrounding
// function and the function literal, and they survive as long as they are accessible.
// intSeq returns another function, which we define anonymously in the body of intSeq.
// The returned function closes over the variable i to form a closure.
func intSeq() func() int {
	i := 0
	return func() int { // anonymous function
		i += 1
		return i
	}
}
<commit_msg>add attributions<commit_after>package main

import "fmt"

type BinOp func(x, y int) int

func main() {
	x, y := 2, 3
	f := CreateBinOp("+")(x, y)
	fmt.Printf("%d + %d = %d\n", x, y, f)

	g := intSeq()
	// everytime we call the function value g we get next int value
	for i := 0; i < 10; i++ {
		fmt.Printf("%v ", g())
	}
}

// notice the type of return value of the function CreateBinOp
// is the same as the type of the BinOp, hence we can replace this with BinOp.
// Hence line 23 is equivalent to line 22
//func CreateBinOp(op string) func(x, y int) int {
func CreateBinOp(op string) BinOp {
	var r BinOp // notice the type of r

	switch op {
	case "+":
		r = func(x, y int) int { return x + y }
	case "-":
		r = func(x, y int) int { return x - y }
	case "*":
		r = func(x, y int) int { return x * y }
	case "/":
		r = func(x, y int) int { return x / y }
	}

	return r
}

// Function literals are closures: they may refer to variables defined in a
// surrounding function. Those variables are then shared between the surrounding
// function and the function literal, and they survive as long as they are accessible.
// intSeq returns another function, which we define anonymously in the body of intSeq.
// The returned function closes over the variable i to form a closure.
// this example ha been taken verbatim from Go by Example: Closures
func intSeq() func() int {
	i := 0
	return func() int { // anonymous function
		i += 1
		return i
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 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 owners

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strings"
)

type Owner struct {
	GitHubUsername string `json:"githubUsername"`
	GerritEmail    string `json:"gerritEmail"`
}

type Entry struct {
	Primary   []Owner `json:"primary"`
	Secondary []Owner `json:"secondary,omitempty"`
}

// match takes a path consisting of the repo name and full path of a file or
// directory within that repo and returns the deepest Entry match in the file
// hierarchy for the given resource.
func match(path string) *Entry {
	var deepestPath string
	for p := range entries {
		if strings.HasPrefix(path, p) && len(p) > len(deepestPath) {
			deepestPath = p
		}
	}
	return entries[deepestPath]
}

// URLPathPrefix is the prefix that should be used when registering Handler.
const URLPathPrefix = "/owners/"

// Handler serves Entry structs as JSON responses given a URL path of a repo and
// file. For example, /owners/go/src/archive/tar/reader.go should return a
// single Entry for that file or 404 if it can't find an owner.
// TODO: Ability to POST a list of paths to match.
func Handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	p := r.URL.Path
	if strings.HasPrefix(p, URLPathPrefix) {
		p = r.URL.Path[len(URLPathPrefix):]
	}
	e := match(p)
	if e == nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(e); err != nil {
		http.Error(w, "unable to encode response", http.StatusInternalServerError)
		return
	}
	w.Write(buf.Bytes())
}
<commit_msg>devapp: log error if encoding owners response fails<commit_after>// Copyright 2018 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 owners

import (
	"bytes"
	"encoding/json"
	"log"
	"net/http"
	"strings"
)

type Owner struct {
	GitHubUsername string `json:"githubUsername"`
	GerritEmail    string `json:"gerritEmail"`
}

type Entry struct {
	Primary   []Owner `json:"primary"`
	Secondary []Owner `json:"secondary,omitempty"`
}

// match takes a path consisting of the repo name and full path of a file or
// directory within that repo and returns the deepest Entry match in the file
// hierarchy for the given resource.
func match(path string) *Entry {
	var deepestPath string
	for p := range entries {
		if strings.HasPrefix(path, p) && len(p) > len(deepestPath) {
			deepestPath = p
		}
	}
	return entries[deepestPath]
}

// URLPathPrefix is the prefix that should be used when registering Handler.
const URLPathPrefix = "/owners/"

// Handler serves Entry structs as JSON responses given a URL path of a repo and
// file. For example, /owners/go/src/archive/tar/reader.go should return a
// single Entry for that file or 404 if it can't find an owner.
// TODO: Ability to POST a list of paths to match.
func Handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	p := r.URL.Path
	if strings.HasPrefix(p, URLPathPrefix) {
		p = r.URL.Path[len(URLPathPrefix):]
	}
	e := match(p)
	if e == nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(e); err != nil {
		http.Error(w, "unable to encode response", http.StatusInternalServerError)
		// TODO: increment expvar for monitoring.
		log.Printf("unable to encode owners response: %v", err)
		return
	}
	w.Write(buf.Bytes())
}
<|endoftext|>


<commit_before><commit_msg>golang tar.Writer doesn't pass through close calls.  compensate.<commit_after><|endoftext|>


<commit_before>package txscript

import (
	"chain/errors"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcutil"
)

// RedeemToPkScript takes a p2sh redeem script
// and returns the pkscript to pay to it.
func RedeemToPkScript(redeem []byte) []byte {
	p2sh, _ := btcutil.NewAddressScriptHash(redeem, &chaincfg.MainNetParams)
	script, _ := txscript.PayToAddrScript(p2sh)
	return script
}

// RedeemScriptFromP2SHSigScript parses the signature script and returns the
// redeem script.
func RedeemScriptFromP2SHSigScript(sigScript []byte) ([]byte, error) {
	opCodes, err := parseScript(sigScript)
	if err != nil {
		return nil, errors.Wrap(err, "decoding redeem script from sig script")
	}

	return opCodes[len(opCodes)-1].data, nil
}

// TODO(tessr): Write BuildP2SHSigScript, which will correlate to
// RedeemScriptFromP2SHSigScript and will do something similar to
// asset.assembleSignatures.
<commit_msg>fedchain/txscript: remove btcd/txscript import<commit_after>package txscript

import (
	"chain/errors"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil"
)

// RedeemToPkScript takes a p2sh redeem script
// and returns the pkscript to pay to it.
func RedeemToPkScript(redeem []byte) []byte {
	p2sh, _ := btcutil.NewAddressScriptHash(redeem, &chaincfg.MainNetParams)
	script, _ := PayToAddrScript(p2sh)
	return script
}

// RedeemScriptFromP2SHSigScript parses the signature script and returns the
// redeem script.
func RedeemScriptFromP2SHSigScript(sigScript []byte) ([]byte, error) {
	opCodes, err := parseScript(sigScript)
	if err != nil {
		return nil, errors.Wrap(err, "decoding redeem script from sig script")
	}

	return opCodes[len(opCodes)-1].data, nil
}

// TODO(tessr): Write BuildP2SHSigScript, which will correlate to
// RedeemScriptFromP2SHSigScript and will do something similar to
// asset.assembleSignatures.
<|endoftext|>


<commit_before>package etreeutils

import (
	"errors"

	"fmt"

	"sort"

	"github.com/beevik/etree"
)

const (
	defaultPrefix = ""
	xmlnsPrefix   = "xmlns"
	xmlPrefix     = "xml"

	XMLNamespace   = "http://www.w3.org/XML/1998/namespace"
	XMLNSNamespace = "http://www.w3.org/2000/xmlns/"
)

var (
	DefaultNSContext = NSContext{
		prefixes: map[string]string{
			defaultPrefix: XMLNamespace,
			xmlPrefix:     XMLNamespace,
			xmlnsPrefix:   XMLNSNamespace,
		},
	}

	EmptyNSContext = NSContext{}

	ErrReservedNamespace       = errors.New("disallowed declaration of reserved namespace")
	ErrInvalidDefaultNamespace = errors.New("invalid default namespace declaration")
	ErrTraversalHalted         = errors.New("traversal halted")
)

type ErrUndeclaredNSPrefix struct {
	Prefix string
}

func (e ErrUndeclaredNSPrefix) Error() string {
	return fmt.Sprintf("undeclared namespace prefix: '%s'", e.Prefix)
}

type NSContext struct {
	prefixes map[string]string
}

func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
	// The subcontext should inherit existing declared prefixes
	prefixes := make(map[string]string, len(ctx.prefixes)+4)
	for k, v := range ctx.prefixes {
		prefixes[k] = v
	}

	// Merge new namespace declarations on top of existing ones.
	for _, attr := range el.Attr {
		if attr.Space == xmlnsPrefix {
			// This attribute is a namespace declaration of the form "xmlns:<prefix>"

			// The 'xml' namespace may only be re-declared with the name 'http://www.w3.org/XML/1998/namespace'
			if attr.Key == xmlPrefix && attr.Value != XMLNamespace {
				return ctx, ErrReservedNamespace
			}

			// The 'xmlns' namespace may not be re-declared
			if attr.Key == xmlnsPrefix {
				return ctx, ErrReservedNamespace
			}

			prefixes[attr.Key] = attr.Value
		} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
			// This attribute is a default namespace declaration

			// The xmlns namespace value may not be declared as the default namespace
			if attr.Value == XMLNSNamespace {
				return ctx, ErrInvalidDefaultNamespace
			}

			prefixes[defaultPrefix] = attr.Value
		}
	}

	return NSContext{prefixes: prefixes}, nil
}

// LookupPrefix attempts to find a declared namespace for the specified prefix. If the prefix
// is an empty string this will be the default namespace for this context. If the prefix is
// undeclared in this context an ErrUndeclaredNSPrefix will be returned.
func (ctx NSContext) LookupPrefix(prefix string) (string, error) {
	if namespace, ok := ctx.prefixes[prefix]; ok {
		return namespace, nil
	}

	return "", ErrUndeclaredNSPrefix{
		Prefix: prefix,
	}
}

// NSIterHandler is a function which is invoked with a element and its surrounding
// NSContext during traversals.
type NSIterHandler func(NSContext, *etree.Element) error

// NSTraverse traverses an element tree, invoking the passed handler for each element
// in the tree.
func NSTraverse(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
	ctx, err := ctx.SubContext(el)
	if err != nil {
		return err
	}

	err = handle(ctx, el)
	if err != nil {
		return err
	}

	// Recursively traverse child elements.
	for _, child := range el.ChildElements() {
		err := NSTraverse(ctx, child, handle)
		if err != nil {
			return err
		}
	}

	return nil
}

// NSDetatch makes a copy of the passed element, and declares any namespaces in
// the passed context onto the new element before returning it.
func NSDetatch(ctx NSContext, el *etree.Element) (*etree.Element, error) {
	ctx, err := ctx.SubContext(el)
	if err != nil {
		return nil, err
	}

	el = el.Copy()

	// Build a new attribute list
	attrs := make([]etree.Attr, 0, len(el.Attr))

	// First copy over anything that isn't a namespace declaration
	for _, attr := range el.Attr {
		if attr.Space == xmlnsPrefix {
			continue
		}

		if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
			continue
		}

		attrs = append(attrs, attr)
	}

	// Append all in-context namespace declarations
	for prefix, namespace := range ctx.prefixes {
		// Skip the implicit "xml" and "xmlns" prefix declarations
		if prefix == xmlnsPrefix || prefix == xmlPrefix {
			continue
		}

		// Also skip declararing the default namespace as XMLNamespace
		if prefix == defaultPrefix && namespace == XMLNamespace {
			continue
		}

		if prefix != defaultPrefix {
			attrs = append(attrs, etree.Attr{
				Space: xmlnsPrefix,
				Key:   prefix,
				Value: namespace,
			})
		} else {
			attrs = append(attrs, etree.Attr{
				Key:   xmlnsPrefix,
				Value: namespace,
			})
		}
	}

	sort.Sort(SortedAttrs(attrs))

	el.Attr = attrs

	return el, nil
}

// NSSelectOne conducts a depth-first search for an element with the specified namespace
// and tag. If such an element is found, a new *etree.Element is returned which is a
// copy of the found element, but with all in-context namespace declarations attached
// to the element as attributes.
func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
	var found *etree.Element

	err := NSFindIterate(el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
		var err error

		found, err = NSDetatch(ctx, el)
		if err != nil {
			return err
		}

		return ErrTraversalHalted
	})

	if err != nil {
		return nil, err
	}

	return found, nil
}

// NSFindIterate behaves identically to NSFindIterateCtx, but uses DefaultNSContext
// as the surrounding context.
func NSFindIterate(el *etree.Element, namespace, tag string, handle NSIterHandler) error {
	return NSFindIterateCtx(DefaultNSContext, el, namespace, tag, handle)
}

// NSFindIterateCtx conducts a depth-first traversal searching for elements with the
// specified tag in the specified namespace. It uses the passed NSContext for prefix
// lookups. For each such element, the passed handler function is invoked. If the
// handler function returns an error traversal is immediately halted. If the error
// returned by the handler is  ErrTraversalHalted then nil will be returned by
// NSFindIterate. If any other error is returned by the handler, that error will be
// returned by NSFindIterate.
func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
	err := NSTraverse(ctx, el, func(ctx NSContext, el *etree.Element) error {
		currentNS, err := ctx.LookupPrefix(el.Space)
		if err != nil {
			return err
		}

		// Base case, el is the sought after element.
		if currentNS == namespace && el.Tag == tag {
			return handle(ctx, el)
		}

		return nil
	})

	if err != nil && err != ErrTraversalHalted {
		return err
	}

	return nil
}

// NSFindOne conducts a depth-first search for the specified element. If such an element
// is found a reference to it is returned.
func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
	var found *etree.Element

	err := NSFindIterate(el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
		found = el
		return ErrTraversalHalted
	})

	if err != nil {
		return nil, err
	}

	return found, nil
}
<commit_msg>Fill in a bunch more etreeutils.NS*Ctx functions<commit_after>package etreeutils

import (
	"errors"

	"fmt"

	"sort"

	"github.com/beevik/etree"
)

const (
	defaultPrefix = ""
	xmlnsPrefix   = "xmlns"
	xmlPrefix     = "xml"

	XMLNamespace   = "http://www.w3.org/XML/1998/namespace"
	XMLNSNamespace = "http://www.w3.org/2000/xmlns/"
)

var (
	DefaultNSContext = NSContext{
		prefixes: map[string]string{
			defaultPrefix: XMLNamespace,
			xmlPrefix:     XMLNamespace,
			xmlnsPrefix:   XMLNSNamespace,
		},
	}

	EmptyNSContext = NSContext{}

	ErrReservedNamespace       = errors.New("disallowed declaration of reserved namespace")
	ErrInvalidDefaultNamespace = errors.New("invalid default namespace declaration")
	ErrTraversalHalted         = errors.New("traversal halted")
)

type ErrUndeclaredNSPrefix struct {
	Prefix string
}

func (e ErrUndeclaredNSPrefix) Error() string {
	return fmt.Sprintf("undeclared namespace prefix: '%s'", e.Prefix)
}

type NSContext struct {
	prefixes map[string]string
}

func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
	// The subcontext should inherit existing declared prefixes
	prefixes := make(map[string]string, len(ctx.prefixes)+4)
	for k, v := range ctx.prefixes {
		prefixes[k] = v
	}

	// Merge new namespace declarations on top of existing ones.
	for _, attr := range el.Attr {
		if attr.Space == xmlnsPrefix {
			// This attribute is a namespace declaration of the form "xmlns:<prefix>"

			// The 'xml' namespace may only be re-declared with the name 'http://www.w3.org/XML/1998/namespace'
			if attr.Key == xmlPrefix && attr.Value != XMLNamespace {
				return ctx, ErrReservedNamespace
			}

			// The 'xmlns' namespace may not be re-declared
			if attr.Key == xmlnsPrefix {
				return ctx, ErrReservedNamespace
			}

			prefixes[attr.Key] = attr.Value
		} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
			// This attribute is a default namespace declaration

			// The xmlns namespace value may not be declared as the default namespace
			if attr.Value == XMLNSNamespace {
				return ctx, ErrInvalidDefaultNamespace
			}

			prefixes[defaultPrefix] = attr.Value
		}
	}

	return NSContext{prefixes: prefixes}, nil
}

// Prefixes returns a copy of this context's prefix map.
func (ctx NSContext) Prefixes() map[string]string {
	prefixes := make(map[string]string, len(ctx.prefixes))
	for k, v := range ctx.prefixes {
		prefixes[k] = v
	}

	return prefixes
}

// LookupPrefix attempts to find a declared namespace for the specified prefix. If the prefix
// is an empty string this will be the default namespace for this context. If the prefix is
// undeclared in this context an ErrUndeclaredNSPrefix will be returned.
func (ctx NSContext) LookupPrefix(prefix string) (string, error) {
	if namespace, ok := ctx.prefixes[prefix]; ok {
		return namespace, nil
	}

	return "", ErrUndeclaredNSPrefix{
		Prefix: prefix,
	}
}

// NSIterHandler is a function which is invoked with a element and its surrounding
// NSContext during traversals.
type NSIterHandler func(NSContext, *etree.Element) error

// NSTraverse traverses an element tree, invoking the passed handler for each element
// in the tree.
func NSTraverse(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
	ctx, err := ctx.SubContext(el)
	if err != nil {
		return err
	}

	err = handle(ctx, el)
	if err != nil {
		return err
	}

	// Recursively traverse child elements.
	for _, child := range el.ChildElements() {
		err := NSTraverse(ctx, child, handle)
		if err != nil {
			return err
		}
	}

	return nil
}

// NSDetatch makes a copy of the passed element, and declares any namespaces in
// the passed context onto the new element before returning it.
func NSDetatch(ctx NSContext, el *etree.Element) (*etree.Element, error) {
	ctx, err := ctx.SubContext(el)
	if err != nil {
		return nil, err
	}

	el = el.Copy()

	// Build a new attribute list
	attrs := make([]etree.Attr, 0, len(el.Attr))

	// First copy over anything that isn't a namespace declaration
	for _, attr := range el.Attr {
		if attr.Space == xmlnsPrefix {
			continue
		}

		if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
			continue
		}

		attrs = append(attrs, attr)
	}

	// Append all in-context namespace declarations
	for prefix, namespace := range ctx.prefixes {
		// Skip the implicit "xml" and "xmlns" prefix declarations
		if prefix == xmlnsPrefix || prefix == xmlPrefix {
			continue
		}

		// Also skip declararing the default namespace as XMLNamespace
		if prefix == defaultPrefix && namespace == XMLNamespace {
			continue
		}

		if prefix != defaultPrefix {
			attrs = append(attrs, etree.Attr{
				Space: xmlnsPrefix,
				Key:   prefix,
				Value: namespace,
			})
		} else {
			attrs = append(attrs, etree.Attr{
				Key:   xmlnsPrefix,
				Value: namespace,
			})
		}
	}

	sort.Sort(SortedAttrs(attrs))

	el.Attr = attrs

	return el, nil
}

// NSSelectOne behaves identically to NSSelectOneCtx, but uses DefaultNSContext as the
// surrounding context.
func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
	return NSSelectOneCtx(DefaultNSContext, el, namespace, tag)
}

// NSSelectOneCtx conducts a depth-first search for an element with the specified namespace
// and tag. If such an element is found, a new *etree.Element is returned which is a
// copy of the found element, but with all in-context namespace declarations attached
// to the element as attributes.
func NSSelectOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
	var found *etree.Element

	err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
		var err error

		found, err = NSDetatch(ctx, el)
		if err != nil {
			return err
		}

		return ErrTraversalHalted
	})

	if err != nil {
		return nil, err
	}

	return found, nil
}

// NSFindIterate behaves identically to NSFindIterateCtx, but uses DefaultNSContext
// as the surrounding context.
func NSFindIterate(el *etree.Element, namespace, tag string, handle NSIterHandler) error {
	return NSFindIterateCtx(DefaultNSContext, el, namespace, tag, handle)
}

// NSFindIterateCtx conducts a depth-first traversal searching for elements with the
// specified tag in the specified namespace. It uses the passed NSContext for prefix
// lookups. For each such element, the passed handler function is invoked. If the
// handler function returns an error traversal is immediately halted. If the error
// returned by the handler is  ErrTraversalHalted then nil will be returned by
// NSFindIterate. If any other error is returned by the handler, that error will be
// returned by NSFindIterate.
func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
	err := NSTraverse(ctx, el, func(ctx NSContext, el *etree.Element) error {
		currentNS, err := ctx.LookupPrefix(el.Space)
		if err != nil {
			return err
		}

		// Base case, el is the sought after element.
		if currentNS == namespace && el.Tag == tag {
			return handle(ctx, el)
		}

		return nil
	})

	if err != nil && err != ErrTraversalHalted {
		return err
	}

	return nil
}

// NSFindOne behaves identically to NSFindOneCtx, but uses DefaultNSContext for
// context.
func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
	return NSFindOneCtx(DefaultNSContext, el, namespace, tag)
}

// NSFindOneCtx conducts a depth-first search for the specified element. If such an element
// is found a reference to it is returned.
func NSFindOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
	var found *etree.Element

	err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
		found = el
		return ErrTraversalHalted
	})

	if err != nil {
		return nil, err
	}

	return found, nil
}

// NSBuildParentContext recurses upward from an element in order to build an NSContext
// for its immediate parent. If the element has no parent DefaultNSContext
// is returned.
func NSBuildParentContext(el *etree.Element) (NSContext, error) {
	parent := el.Parent()
	if parent == nil {
		return DefaultNSContext, nil
	}

	ctx, err := NSBuildParentContext(parent)

	if err != nil {
		return ctx, err
	}

	return ctx.SubContext(parent)
}
<|endoftext|>


<commit_before>// Package retry contains set of cliware middlewares and other utilities used
// for retry logic. Any of it to actually be applied, client has to use
// RoundTripper returned via retry.NewRoundTripper function as it implements
// all logic and applies all config values set by middlewares.
//
// This RoundTripper wraps your actual round tripper and uses it to send
// requests, so you can still apply you custom settings.
//
// Setting this RoundTripper is something that client library should do
// (like go.delic.rs/gwc), so, in general, for writing simple endpoints
// there should be no need to use it.
package retry
<commit_msg>Fix path in doc for retry<commit_after>// Package retry contains set of cliware middlewares and other utilities used
// for retry logic. Any of it to actually be applied, client has to use
// RoundTripper returned via retry.NewRoundTripper function as it implements
// all logic and applies all config values set by middlewares.
//
// This RoundTripper wraps your actual round tripper and uses it to send
// requests, so you can still apply you custom settings.
//
// Setting this RoundTripper is something that client library should do
// (like github.com/delicb/gwc), so, in general, for writing simple endpoints
// there should be no need to use it.
package retry
<|endoftext|>


<commit_before>package fap

import (
	"bytes"
	"errors"
	"fmt"
	"strings"
	"time"
)

type FapPacket struct {
	PacketType uint // See const
	OrigPacket string

	Header string
	Body   string

	SrcCallsign string
	DstCallsign string
	Path        []string

	Latitude  float64
	Longitude float64
	Format    uint // See const

	PosResolution float64
	PosAmbiguity  uint
	DaoDatumByte  byte
	Altitude      float64
	Course        uint
	Speed         float64

	SymbolTable byte
	SymbolCode  byte

	Messaging   bool
	Destination string
	Message     string
	MessageAck  string
	MessageNack string
	MessageId   string

	Comment          string
	ObjectOrItemName string
	Alive            bool
	GpsFixStatus     bool
	RadioRange       uint
	Phg              string
	Timestamp        time.Time
	NmeaChecksumOk   bool

	WxReport  string
	Telemetry string

	Messagebits  string
	Status       string
	Capabilities []string
}

func (p *FapPacket) MicEMessage() string {
	if p.Messagebits == "" {
		return ""
	}

	return MicEMbitsToMessage(p.Messagebits)
}

func (a *FapPacket) Distance(b *FapPacket) (float64, error) {
	if b == nil {
		return 0, errors.New("Distance between A and nil is undefined")
	}
	if a.Latitude == 0 || a.Longitude == 0 ||
		b.Longitude == 0 || b.Latitude == 0 {
		return 0, errors.New("One or more components is nil when calculating distance")
	}

	return Distance(
		a.Latitude, a.Longitude,
		b.Latitude, b.Longitude,
	), nil
}

func (a *FapPacket) Direction(b *FapPacket) (float64, error) {
	if b == nil {
		return 0, errors.New("Direction between A and nil is undefined")
	}
	if a.Latitude == 0 || a.Longitude == 0 ||
		b.Longitude == 0 || b.Latitude == 0 {
		return 0, errors.New("One or more components is nil when calculating direction")
	}

	return Direction(
		a.Latitude, a.Longitude,
		b.Latitude, b.Longitude,
	), nil
}

var cardinals = []string{"N", "NE", "E", "SE", "S", "SW", "W", "NW"}

func (a *FapPacket) HumanReadableDirection(b *FapPacket) (string, error) {
	degrees, err := a.Direction(b)
	if err != nil {
		return "", err
	}

	return cardinals[int((degrees+22.5)/45.0)%8], err
}

func (p *FapPacket) String() string {
	buffer := bytes.NewBufferString("")

	if p.PacketType == OBJECT {
		fmt.Fprintf(buffer, "%s (via %s)\n", strings.TrimSpace(p.ObjectOrItemName), p.SrcCallsign)
	} else {
		fmt.Fprintf(buffer, "%s\n", p.SrcCallsign)
	}

	if !p.Timestamp.IsZero() {
		fmt.Fprintf(buffer, "Time: %s\n", p.Timestamp)
	}

	if len(p.Path) > 0 {
		fmt.Fprintf(buffer, "Path: %q\n", p.Path)
	}

	fmt.Fprintf(buffer, "Pos: %f,%f\n", p.Latitude, p.Longitude)

	fmt.Fprintf(buffer, "Speed: %.0fkm/h\n", p.Speed)

	if p.Comment != "" {
		fmt.Fprintf(buffer, "Comment: %s\n", strings.TrimSpace(p.Comment))
	}

	if p.Status != "" {
		fmt.Fprintf(buffer, "Status: %s\n", strings.TrimSpace(p.Status))
	}

	if p.Messagebits != "" {
		fmt.Fprintf(buffer, "Mic-E: %s\n", p.MicEMessage())
	}

	return buffer.String()
}
<commit_msg>Fix Distance and Direction methods. Add HasLocation() method. Add some documentation.<commit_after>package fap

import (
	"bytes"
	"errors"
	"fmt"
	"strings"
	"time"
)

// FapPacket is the APRS packet type
type FapPacket struct {
	PacketType uint // See const
	OrigPacket string

	Header string
	Body   string

	SrcCallsign string
	DstCallsign string
	Path        []string

	Latitude  float64
	Longitude float64
	Format    uint // See const

	PosResolution float64
	PosAmbiguity  uint
	DaoDatumByte  byte
	Altitude      float64
	Course        uint
	Speed         float64

	SymbolTable byte
	SymbolCode  byte

	Messaging   bool
	Destination string
	Message     string
	MessageAck  string
	MessageNack string
	MessageId   string

	Comment          string
	ObjectOrItemName string
	Alive            bool
	GpsFixStatus     bool
	RadioRange       uint
	Phg              string
	Timestamp        time.Time
	NmeaChecksumOk   bool

	WxReport  string
	Telemetry string

	Messagebits  string
	Status       string
	Capabilities []string
}

// HasLocation returns true if packet has location
// data.
func (p *FapPacket) HasLocation() bool {
	return (p.Latitude != 0 && p.Longitude != 0)
}

// MicEMessage returns the texual Mic-E message.
func (p *FapPacket) MicEMessage() string {
	if p.Messagebits == "" {
		return ""
	}

	return MicEMbitsToMessage(p.Messagebits)
}

// Distance returns the distance to the given packet b in km.
func (a *FapPacket) Distance(b *FapPacket) (float64, error) {
	if b == nil {
		return 0, errors.New("Distance between A and nil is undefined")
	}
	if a.Latitude == 0 || a.Longitude == 0 ||
		b.Longitude == 0 || b.Latitude == 0 {
		return 0, errors.New("One or more components is nil when calculating distance")
	}

	return Distance(
		a.Longitude, a.Latitude,
		b.Longitude, b.Latitude,
	), nil
}

func (a *FapPacket) Direction(b *FapPacket) (float64, error) {
	if b == nil {
		return 0, errors.New("Direction between A and nil is undefined")
	}
	if a.Latitude == 0 || a.Longitude == 0 ||
		b.Longitude == 0 || b.Latitude == 0 {
		return 0, errors.New("One or more components is nil when calculating direction")
	}

	return Direction(
		a.Longitude, a.Latitude,
		b.Longitude, b.Latitude,
	), nil
}

var cardinals = []string{"N", "NE", "E", "SE", "S", "SW", "W", "NW"}

func (a *FapPacket) HumanReadableDirection(b *FapPacket) (string, error) {
	degrees, err := a.Direction(b)
	if err != nil {
		return "", err
	}

	return cardinals[int((degrees+22.5)/45.0)%8], err
}

func (p *FapPacket) String() string {
	buffer := bytes.NewBufferString("")

	if p.PacketType == OBJECT {
		fmt.Fprintf(buffer, "%s (via %s)\n", strings.TrimSpace(p.ObjectOrItemName), p.SrcCallsign)
	} else {
		fmt.Fprintf(buffer, "%s\n", p.SrcCallsign)
	}

	if !p.Timestamp.IsZero() {
		fmt.Fprintf(buffer, "Time: %s\n", p.Timestamp)
	}

	if len(p.Path) > 0 {
		fmt.Fprintf(buffer, "Path: %q\n", p.Path)
	}

	if p.HasLocation() {
		fmt.Fprintf(buffer, "Pos: %f,%f\n", p.Latitude, p.Longitude)
	}

	fmt.Fprintf(buffer, "Speed: %.0fkm/h\n", p.Speed)

	if p.Comment != "" {
		fmt.Fprintf(buffer, "Comment: %s\n", strings.TrimSpace(p.Comment))
	}

	if p.Status != "" {
		fmt.Fprintf(buffer, "Status: %s\n", strings.TrimSpace(p.Status))
	}

	if p.Messagebits != "" {
		fmt.Fprintf(buffer, "Mic-E: %s\n", p.MicEMessage())
	}

	return buffer.String()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"strings"
)

// Config represents Slack configuration data
type Conf struct {
	Token    string `json:"token"`
	Channel  string `json:"channel"`
	Username string `json:"username"`
}

var conf Conf

func main() {

	usr, err := user.Current()
	if err != nil {
		os.Stderr.WriteString("Can't get current user info... Are you an alien?\n")
		os.Exit(1)
	}
	confFile := usr.HomeDir + "/.slack-cli.json"

	file, err := ioutil.ReadFile(confFile)
	if err != nil {
		os.Stderr.WriteString("Can't find ~/.slack-cli.json.\n")
		os.Exit(1)
	}

	err = json.Unmarshal(file, &conf)
	if err != nil {
		os.Stderr.WriteString("Can't parse ~/.slack-cli.json. Is the format correct?\n")
		os.Exit(1)
	}

	if len(os.Args) < 2 {
		os.Stderr.WriteString("Usage: slack-cli MESSAGE-TO-SEND\n")
		os.Exit(1)
	}

	query := fmt.Sprintf("token=%s&channel=%s&username=%s&text=%s",
		conf.Token,
		conf.Channel,
		conf.Username,
		strings.Join(os.Args[1:], " "))

	body := strings.NewReader(query)
	req, err := http.NewRequest("POST", "https://slack.com/api/chat.postMessage", body)
	if err != nil {
		os.Stderr.WriteString("Can't post to Slack API.\n")
		os.Exit(1)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		os.Stderr.WriteString("Can't post to Slack API.\n")
		os.Exit(1)
	}
	defer resp.Body.Close()
}
<commit_msg>Config to conf for consistency (more)<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"strings"
)

// Conf represents Slack configuration data
type Conf struct {
	Token    string `json:"token"`
	Channel  string `json:"channel"`
	Username string `json:"username"`
}

var conf Conf

func main() {

	usr, err := user.Current()
	if err != nil {
		os.Stderr.WriteString("Can't get current user info... Are you an alien?\n")
		os.Exit(1)
	}
	confFile := usr.HomeDir + "/.slack-cli.json"

	file, err := ioutil.ReadFile(confFile)
	if err != nil {
		os.Stderr.WriteString("Can't find ~/.slack-cli.json.\n")
		os.Exit(1)
	}

	err = json.Unmarshal(file, &conf)
	if err != nil {
		os.Stderr.WriteString("Can't parse ~/.slack-cli.json. Is the format correct?\n")
		os.Exit(1)
	}

	if len(os.Args) < 2 {
		os.Stderr.WriteString("Usage: slack-cli MESSAGE-TO-SEND\n")
		os.Exit(1)
	}

	query := fmt.Sprintf("token=%s&channel=%s&username=%s&text=%s",
		conf.Token,
		conf.Channel,
		conf.Username,
		strings.Join(os.Args[1:], " "))

	body := strings.NewReader(query)
	req, err := http.NewRequest("POST", "https://slack.com/api/chat.postMessage", body)
	if err != nil {
		os.Stderr.WriteString("Can't post to Slack API.\n")
		os.Exit(1)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		os.Stderr.WriteString("Can't post to Slack API.\n")
		os.Exit(1)
	}
	defer resp.Body.Close()
}
<|endoftext|>


<commit_before>package op

// This defines how we do projected gradient.
type ProjectedGradient struct {
	projector          *Projection
	beta, sigma, alpha float32
}

type vgpair struct {
	value    float32
	gradient Parameter
}

func NewProjectedGradient(projector *Projection, beta, sigma, alpha float32) *ProjectedGradient {
	return &ProjectedGradient{
		projector: projector,
		beta:      beta,
		sigma:     sigma,
		alpha:     alpha,
	}
}

// This implementation is based on "Projected Gradient Methods for Non-negative Matrix
// Factorization" by Chih-Jen Lin. Particularly it is based on the discription of an
// improved projected gradient method in page 10 of that paper.
func (pg *ProjectedGradient) Minimize(loss Function, stop StopCriteria, vec Parameter) (float32, error) {

	stt := vec

	// Remember to clip the point before we do any thing.
	pg.projector.ClipPoint(stt)

	nxt := stt.CloneWithoutCopy()
	cdd := stt.CloneWithoutCopy()
	Fill(nxt, 0)

	// Evaluate once
	ovalgrad := &vgpair{value: 0, gradient: stt.CloneWithoutCopy()}
	evaluate(loss, stt, ovalgrad)
	nvalgrad := &vgpair{value: 0, gradient: stt.CloneWithoutCopy()}
	tvalgrad := &vgpair{value: 0, gradient: stt.CloneWithoutCopy()}

	alpha := pg.alpha

	for k := 0; !stop.Done(stt, ovalgrad.value, ovalgrad.gradient); k += 1 {

		newPoint(stt, nxt, ovalgrad.gradient, alpha, pg.projector)
		evaluate(loss, nxt, nvalgrad)

		if pg.isGoodStep(stt, nxt, ovalgrad, nvalgrad) {
			newPoint(stt, cdd, ovalgrad.gradient, alpha/pg.beta, pg.projector)
			evaluate(loss, cdd, tvalgrad)
			for pg.isGoodStep(stt, cdd, ovalgrad, tvalgrad) {
				nxt, cdd = cdd, nxt
				nvalgrad, tvalgrad = tvalgrad, nvalgrad
				// Now increase alpha as much as we can.
				alpha /= pg.beta
				newPoint(stt, cdd, ovalgrad.gradient, alpha/pg.beta, pg.projector)
				evaluate(loss, cdd, tvalgrad)
				if pg.isTooClose(cdd, nxt) {
					break
				}
			}
		} else {
			// Now we decrease alpha barely enough to make sufficient decrease
			// of the objective value.
			for !pg.isGoodStep(stt, nxt, ovalgrad, nvalgrad) {
				alpha *= pg.beta
				newPoint(stt, nxt, ovalgrad.gradient, alpha, pg.projector)
				evaluate(loss, nxt, nvalgrad)
			}
		}

		// Now we arrive at a point satisfies sufficient decrease condition.
		// Swap the wts and gradient for the next round.
		stt, nxt = nxt, stt
		ovalgrad, nvalgrad = nvalgrad, ovalgrad
	}

	// Originally stt == vec, but stt may be swapped to newly created param (nxt). So copy it to output param here.
	for it := vec.IndexIterator(); it.Next(); {
		i := it.Index()
		vec.Set(i, stt.Get(i))
	}

	// This is so that we can reuse the step size in next round.
	pg.alpha = alpha

	// Return the final loss func value and error.
	return ovalgrad.value, nil
}

// This implements the sufficient decrease condition described in Eq (13)
func (pg *ProjectedGradient) isGoodStep(owts, nwts Parameter, ovg, nvg *vgpair) bool {
	valdiff := nvg.value - ovg.value
	sum := float64(0)
	for it := owts.IndexIterator(); it.Next(); {
		i := it.Index()
		sum += float64(ovg.gradient.Get(i) * (nwts.Get(i) - owts.Get(i)))
	}
	return valdiff <= pg.sigma*float32(sum)
}

func (pg *ProjectedGradient) isTooClose(owts, nwts Parameter) bool {
	sum := float64(0)
	for it := owts.IndexIterator(); it.Next(); {
		i := it.Index()
		diff := float64(owts.Get(i) - nwts.Get(i))
		sum += diff * diff
	}
	return sum < 1e-16*float64(owts.IndexIterator().Size())
}

// This creates a new point based on current point, step size and gradient.
func newPoint(owts, nwts, grad Parameter, alpha float32, projector *Projection) {
	for it := owts.IndexIterator(); it.Next(); {
		i := it.Index()
		nwts.Set(i, owts.Get(i)-alpha*grad.Get(i))
	}
	projector.ClipPoint(nwts)
}

func evaluate(loss Function, stt Parameter, ovalgrad *vgpair) {
	Fill(ovalgrad.gradient, 0)
	ovalgrad.value = loss.Evaluate(stt, ovalgrad.gradient)
}
<commit_msg>op: Alternative projected gradient implementation<commit_after>package op

// This defines how we do projected gradient.
type ProjectedGradient struct {
	projector          *Projection
	beta, sigma, alpha float32
}

type vgpair struct {
	value    float32
	gradient Parameter
}

func NewProjectedGradient(projector *Projection, beta, sigma, alpha float32) *ProjectedGradient {
	return &ProjectedGradient{
		projector: projector,
		beta:      beta,
		sigma:     sigma,
		alpha:     alpha,
	}
}

// This implementation is based on "Projected Gradient Methods for Non-negative Matrix
// Factorization" by Chih-Jen Lin. Particularly it is based on the discription of an
// improved projected gradient method in page 10 of that paper.
func (pg *ProjectedGradient) Minimize(loss Function, stop StopCriteria, vec Parameter) (float32, error) {

	stt := vec

	// Remember to clip the point before we do any thing.
	pg.projector.ClipPoint(stt)

	nxt := stt.CloneWithoutCopy()
	Fill(nxt, 0)

	// Evaluate once
	ovalgrad := &vgpair{value: 0, gradient: stt.CloneWithoutCopy()}
	evaluate(loss, stt, ovalgrad)
	nvalgrad := &vgpair{value: 0, gradient: stt.CloneWithoutCopy()}

	pg.projector.ClipGradient(stt, ovalgrad.gradient)
	alpha_moves := []int{0, 0, 0, 0, 0}
	current_alpha := pg.alpha

	for k := 0; !stop.Done(stt, ovalgrad.value, ovalgrad.gradient); k += 1 {
		idx := k % len(alpha_moves)
		alpha := current_alpha
		alpha_moves[idx] = 0;

		newPoint(stt, nxt, ovalgrad.gradient, alpha, pg.projector)
		evaluate(loss, nxt, nvalgrad)

		if pg.isGoodStep(stt, nxt, ovalgrad, nvalgrad) {
			alpha_moves[idx] = 1
			move_sum := 0
			for i := 0; i < len(alpha_moves); i++ {
				move_sum += alpha_moves[i];
			}
			if (move_sum > 1) {
				alpha /= pg.beta
				for i := 0; i < len(alpha_moves); i++ {
					alpha_moves[i] = 0
				}
			}
		} else {
			// Now we decrease alpha barely enough to make sufficient decrease
			// of the objective value.
			for !pg.isGoodStep(stt, nxt, ovalgrad, nvalgrad) {
				alpha *= pg.beta
				alpha_moves[idx] -= 1
				newPoint(stt, nxt, ovalgrad.gradient, alpha, pg.projector)
				evaluate(loss, nxt, nvalgrad)
			}
		}

		current_alpha = alpha
		// Now we arrive at a point satisfies sufficient decrease condition.
		// Swap the wts and gradient for the next round.
		stt, nxt = nxt, stt
		ovalgrad, nvalgrad = nvalgrad, ovalgrad
		pg.projector.ClipGradient(stt, ovalgrad.gradient)
	}

	// Originally stt == vec, but stt may be swapped to newly created param (nxt). So copy it to output param here.
	for it := vec.IndexIterator(); it.Next(); {
		i := it.Index()
		vec.Set(i, stt.Get(i))
	}

	// This is so that we can reuse the step size in next round.
	// pg.alpha = current_alpha

	// Return the final loss func value and error.
	return ovalgrad.value, nil
}

// This implements the sufficient decrease condition described in Eq (13)
func (pg *ProjectedGradient) isGoodStep(owts, nwts Parameter, ovg, nvg *vgpair) bool {
	valdiff := nvg.value - ovg.value
	sum := float64(0)
	for it := owts.IndexIterator(); it.Next(); {
		i := it.Index()
		sum += float64(ovg.gradient.Get(i) * (nwts.Get(i) - owts.Get(i)))
	}
	return valdiff <= pg.sigma*float32(sum)
}

func (pg *ProjectedGradient) isTooClose(owts, nwts Parameter) bool {
	sum := float64(0)
	for it := owts.IndexIterator(); it.Next(); {
		i := it.Index()
		diff := float64(owts.Get(i) - nwts.Get(i))
		sum += diff * diff
	}
	return sum < 1e-16*float64(owts.IndexIterator().Size())
}

// This creates a new point based on current point, step size and gradient.
func newPoint(owts, nwts, grad Parameter, alpha float32, projector *Projection) {
	for it := owts.IndexIterator(); it.Next(); {
		i := it.Index()
		nwts.Set(i, owts.Get(i)-alpha*grad.Get(i))
	}
	projector.ClipPoint(nwts)
}

func evaluate(loss Function, stt Parameter, ovalgrad *vgpair) {
	Fill(ovalgrad.gradient, 0)
	ovalgrad.value = loss.Evaluate(stt, ovalgrad.gradient)
}
<|endoftext|>


<commit_before>package events

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/rancherio/go-machine-service/locks"
)

const RancherIPLabelKey = "io.rancher.container.ip"
const RancherSystemLabelKey = "io.rancher.container.system"
const RancherIPEnvKey = "RANCHER_IP="
const RancherNameserver = "169.254.169.250"
const RancherDomain = "rancher.internal"
const RancherDns = "io.rancher.container.dns"

type StartHandler struct {
	Client            SimpleDockerClient
	ContainerStateDir string
}

func getDnsSearch(container *docker.Container) []string {
	var defaultDomains []string
	var svcNameSpace string
	var stackNameSpace string

	//from labels - for upgraded systems
	if container.Config.Labels != nil {
		if value, ok := container.Config.Labels["io.rancher.stack_service.name"]; ok {
			splitted := strings.Split(value, "/")
			svc := strings.ToLower(splitted[1])
			stack := strings.ToLower(splitted[0])
			svcNameSpace = svc + "." + stack + "." + RancherDomain
			stackNameSpace = stack + "." + RancherDomain
			defaultDomains = append(defaultDomains, svcNameSpace)
			defaultDomains = append(defaultDomains, stackNameSpace)
		}
	}

	//from search domains
	if container.HostConfig.DNSSearch != nil {
		for _, domain := range container.HostConfig.DNSSearch {
			if domain != svcNameSpace && domain != stackNameSpace {
				defaultDomains = append(defaultDomains, domain)
			}
		}
	}

	// default rancher domain
	defaultDomains = append(defaultDomains, RancherDomain)
	return defaultDomains
}

func setupResolvConf(container *docker.Container) error {
	if _, ok := container.Config.Labels[RancherSystemLabelKey]; ok {
		return nil
	}

	p := container.ResolvConfPath
	input, err := os.Open(p)
	if err != nil {
		return err
	}

	defer input.Close()

	var buffer bytes.Buffer
	scanner := bufio.NewScanner(input)
	searchSet := false
	nameserverSet := false
	for scanner.Scan() {
		text := scanner.Text()

		if strings.Contains(text, RancherNameserver) {
			nameserverSet = true
		} else if strings.HasPrefix(text, "nameserver") {
			text = "# " + text
		}

		if strings.HasPrefix(text, "search") {
			for _, domain := range getDnsSearch(container) {
				if strings.Contains(text, " "+domain) {
					continue
				}
				text = text + " " + domain
			}
			searchSet = true
		}

		if _, err := buffer.Write([]byte(text)); err != nil {
			return err
		}

		if _, err := buffer.Write([]byte("\n")); err != nil {
			return err
		}
	}

	if !searchSet {
		buffer.Write([]byte("search " + strings.ToLower(strings.Join(getDnsSearch(container), " "))))
		buffer.Write([]byte("\n"))
	}

	if !nameserverSet {
		buffer.Write([]byte("nameserver "))
		buffer.Write([]byte(RancherNameserver))
		buffer.Write([]byte("\n"))
	}

	input.Close()
	return ioutil.WriteFile(p, buffer.Bytes(), 0666)
}

func (h *StartHandler) Handle(event *docker.APIEvents) error {
	// Note: event.ID == container's ID
	lock := locks.Lock("start." + event.ID)
	if lock == nil {
		log.Debugf("Container locked. Can't run StartHandler. ID: [%s]", event.ID)
		return nil
	}
	defer lock.Unlock()

	c, err := h.Client.InspectContainer(event.ID)
	if err != nil {
		return err
	}

	rancherIP, err := h.getRancherIP(c)
	if err != nil {
		return err
	}

	if rancherIP == "" {
		if c.Config.Labels[RancherDns] == "true" {
			return setupResolvConf(c)
		}
		return nil
	}

	if !c.State.Running {
		log.Infof("Container [%s] not running. Can't assign IP [%s].", c.ID, rancherIP)
		return nil
	}

	pid := c.State.Pid
	log.Infof("Assigning IP [%s], ContainerId [%s], Pid [%v]", rancherIP, event.ID, pid)
	if err := configureIP(strconv.Itoa(pid), rancherIP); err != nil {
		// If it stopped running, don't return error
		c, inspectErr := h.Client.InspectContainer(event.ID)
		if inspectErr != nil {
			log.Warn("Failed to inspect container: ", event.ID, inspectErr)
			return err
		}

		if !c.State.Running {
			log.Infof("Container [%s] not running. Can't assign IP [%s].", c.ID, rancherIP)
			return nil
		}

		return err
	}

	if c.Config.Labels[RancherDns] == "false" {
		return nil
	}

	return setupResolvConf(c)
}

func (h *StartHandler) getRancherIP(c *docker.Container) (string, error) {
	if ip, ok := c.Config.Labels[RancherIPLabelKey]; ok {
		return ip, nil
	}
	for _, env := range c.Config.Env {
		if strings.HasPrefix(env, RancherIPEnvKey) {
			return strings.TrimPrefix(env, RancherIPEnvKey), nil
		}
	}

	filePath := path.Join(h.ContainerStateDir, c.ID)
	if _, err := os.Stat(filePath); err == nil {
		file, e := ioutil.ReadFile(filePath)
		if e != nil {
			return "", fmt.Errorf("Error reading file for container %s: %v", c.ID, e)
		}
		var instanceData instance
		jerr := json.Unmarshal(file, &instanceData)
		if jerr != nil {
			return "", fmt.Errorf("Error unmarshalling json for container %s: %v", c.ID, jerr)
		}

		if len(instanceData.Nics) > 0 {
			nic := instanceData.Nics[0]
			var ipData ipAddress
			for _, i := range nic.IpAddresses {
				if i.Role == "primary" {
					ipData = i
					break
				}
			}
			if ipData.Address != "" {
				return ipData.Address + "/" + strconv.Itoa(ipData.Subnet.CidrSize), nil
			}
		}
	}

	return "", nil
}

func configureIP(pid string, ip string) error {
	command := buildCommand(pid, ip)

	output, err := command.CombinedOutput()
	log.Debugf(string(output))
	if err != nil {
		return err
	}

	return nil
}

var buildCommand = func(pid string, ip string) *exec.Cmd {
	return exec.Command("net-util.sh", "-p", pid, "-i", ip)
}

type subnet struct {
	CidrSize int
}

type ipAddress struct {
	Address string
	Subnet  subnet
	Role    string
}

type nic struct {
	IpAddresses []ipAddress `json:"ipAddresses"`
}

type instance struct {
	Nics []nic
}
<commit_msg>Removed service name space from search domain<commit_after>package events

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/rancherio/go-machine-service/locks"
)

const RancherIPLabelKey = "io.rancher.container.ip"
const RancherSystemLabelKey = "io.rancher.container.system"
const RancherIPEnvKey = "RANCHER_IP="
const RancherNameserver = "169.254.169.250"
const RancherDomain = "rancher.internal"
const RancherDns = "io.rancher.container.dns"

type StartHandler struct {
	Client            SimpleDockerClient
	ContainerStateDir string
}

func getDnsSearch(container *docker.Container) []string {
	var defaultDomains []string
	var svcNameSpace string
	var stackNameSpace string

	//from labels - for upgraded systems
	if container.Config.Labels != nil {
		if value, ok := container.Config.Labels["io.rancher.stack_service.name"]; ok {
			splitted := strings.Split(value, "/")
			stack := strings.ToLower(splitted[0])
			stackNameSpace = stack + "." + RancherDomain
			defaultDomains = append(defaultDomains, stackNameSpace)
		}
	}

	//from search domains
	if container.HostConfig.DNSSearch != nil {
		for _, domain := range container.HostConfig.DNSSearch {
			if domain != svcNameSpace && domain != stackNameSpace {
				defaultDomains = append(defaultDomains, domain)
			}
		}
	}

	// default rancher domain
	defaultDomains = append(defaultDomains, RancherDomain)
	return defaultDomains
}

func setupResolvConf(container *docker.Container) error {
	if _, ok := container.Config.Labels[RancherSystemLabelKey]; ok {
		return nil
	}

	p := container.ResolvConfPath
	input, err := os.Open(p)
	if err != nil {
		return err
	}

	defer input.Close()

	var buffer bytes.Buffer
	scanner := bufio.NewScanner(input)
	searchSet := false
	nameserverSet := false
	for scanner.Scan() {
		text := scanner.Text()

		if strings.Contains(text, RancherNameserver) {
			nameserverSet = true
		} else if strings.HasPrefix(text, "nameserver") {
			text = "# " + text
		}

		if strings.HasPrefix(text, "search") {
			for _, domain := range getDnsSearch(container) {
				if strings.Contains(text, " "+domain) {
					continue
				}
				text = text + " " + domain
			}
			searchSet = true
		}

		if _, err := buffer.Write([]byte(text)); err != nil {
			return err
		}

		if _, err := buffer.Write([]byte("\n")); err != nil {
			return err
		}
	}

	if !searchSet {
		buffer.Write([]byte("search " + strings.ToLower(strings.Join(getDnsSearch(container), " "))))
		buffer.Write([]byte("\n"))
	}

	if !nameserverSet {
		buffer.Write([]byte("nameserver "))
		buffer.Write([]byte(RancherNameserver))
		buffer.Write([]byte("\n"))
	}

	input.Close()
	return ioutil.WriteFile(p, buffer.Bytes(), 0666)
}

func (h *StartHandler) Handle(event *docker.APIEvents) error {
	// Note: event.ID == container's ID
	lock := locks.Lock("start." + event.ID)
	if lock == nil {
		log.Debugf("Container locked. Can't run StartHandler. ID: [%s]", event.ID)
		return nil
	}
	defer lock.Unlock()

	c, err := h.Client.InspectContainer(event.ID)
	if err != nil {
		return err
	}

	rancherIP, err := h.getRancherIP(c)
	if err != nil {
		return err
	}

	if rancherIP == "" {
		if c.Config.Labels[RancherDns] == "true" {
			return setupResolvConf(c)
		}
		return nil
	}

	if !c.State.Running {
		log.Infof("Container [%s] not running. Can't assign IP [%s].", c.ID, rancherIP)
		return nil
	}

	pid := c.State.Pid
	log.Infof("Assigning IP [%s], ContainerId [%s], Pid [%v]", rancherIP, event.ID, pid)
	if err := configureIP(strconv.Itoa(pid), rancherIP); err != nil {
		// If it stopped running, don't return error
		c, inspectErr := h.Client.InspectContainer(event.ID)
		if inspectErr != nil {
			log.Warn("Failed to inspect container: ", event.ID, inspectErr)
			return err
		}

		if !c.State.Running {
			log.Infof("Container [%s] not running. Can't assign IP [%s].", c.ID, rancherIP)
			return nil
		}

		return err
	}

	if c.Config.Labels[RancherDns] == "false" {
		return nil
	}

	return setupResolvConf(c)
}

func (h *StartHandler) getRancherIP(c *docker.Container) (string, error) {
	if ip, ok := c.Config.Labels[RancherIPLabelKey]; ok {
		return ip, nil
	}
	for _, env := range c.Config.Env {
		if strings.HasPrefix(env, RancherIPEnvKey) {
			return strings.TrimPrefix(env, RancherIPEnvKey), nil
		}
	}

	filePath := path.Join(h.ContainerStateDir, c.ID)
	if _, err := os.Stat(filePath); err == nil {
		file, e := ioutil.ReadFile(filePath)
		if e != nil {
			return "", fmt.Errorf("Error reading file for container %s: %v", c.ID, e)
		}
		var instanceData instance
		jerr := json.Unmarshal(file, &instanceData)
		if jerr != nil {
			return "", fmt.Errorf("Error unmarshalling json for container %s: %v", c.ID, jerr)
		}

		if len(instanceData.Nics) > 0 {
			nic := instanceData.Nics[0]
			var ipData ipAddress
			for _, i := range nic.IpAddresses {
				if i.Role == "primary" {
					ipData = i
					break
				}
			}
			if ipData.Address != "" {
				return ipData.Address + "/" + strconv.Itoa(ipData.Subnet.CidrSize), nil
			}
		}
	}

	return "", nil
}

func configureIP(pid string, ip string) error {
	command := buildCommand(pid, ip)

	output, err := command.CombinedOutput()
	log.Debugf(string(output))
	if err != nil {
		return err
	}

	return nil
}

var buildCommand = func(pid string, ip string) *exec.Cmd {
	return exec.Command("net-util.sh", "-p", pid, "-i", ip)
}

type subnet struct {
	CidrSize int
}

type ipAddress struct {
	Address string
	Subnet  subnet
	Role    string
}

type nic struct {
	IpAddresses []ipAddress `json:"ipAddresses"`
}

type instance struct {
	Nics []nic
}
<|endoftext|>


<commit_before>package example

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"testing"

	"github.com/juju/errors"
	"github.com/myitcv/neovim"
	. "gopkg.in/check.v1"
)

type ExampleTest struct {
	client *neovim.Client
	nvim   *exec.Cmd
	plug   *Example
}

func Test(t *testing.T) { TestingT(t) }

var _ = Suite(&ExampleTest{})

func (t *ExampleTest) SetUpTest(c *C) {
	t.nvim = exec.Command(os.Getenv("NEOVIM_BIN"), "-u", "/dev/null")
	t.nvim.Dir = "/tmp"
	client, err := neovim.NewCmdClient(neovim.NullInitMethod, t.nvim, nil)
	if err != nil {
		log.Fatalf("Could not setup client: %v", errors.Details(err))
	}
	client.PanicOnError = true
	t.client = client
	client.Run()

	plug := &Example{}
	err = plug.Init(t.client, log.New(os.Stderr, "", log.LstdFlags))
	if err != nil {
		log.Fatalf("Could not Init plugin: %v\n", err)
	}
	t.plug = plug
}

func (t *ExampleTest) TearDownTest(c *C) {
	err := t.plug.Shutdown()
	if err != nil {
		log.Fatalf("Could not Shutdown plugin: %v\n", err)
	}
	done := make(chan struct{})
	go func() {
		state, err := t.nvim.Process.Wait()
		if err != nil {
			log.Fatalf("Process did not exit cleanly: %v, %v\n", err, state)
		}
		done <- struct{}{}
	}()
	err = t.client.Close()
	if err != nil {
		log.Fatalf("Could not close client: %v\n", err)
	}
	<-done
}

func (t *ExampleTest) TestGetANumber(c *C) {
	t.client.RegisterSyncRequestHandler("GetANumber", t.plug.newGetANumberResponder)
	topic := "GetANumber"
	commandDef := fmt.Sprintf(`call remote#define#FunctionOnChannel(1, "%v", 1, "%v", {})`, topic, topic)
	_ = t.client.Command(commandDef)
	res, _ := t.client.Eval(`GetANumber()`)
	c.Assert(res, Equals, int64(42))
}
<commit_msg>Dont require NEOVIM_BIN for example<commit_after>package example

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"testing"

	"github.com/juju/errors"
	"github.com/myitcv/neovim"
	. "gopkg.in/check.v1"
)

type ExampleTest struct {
	client *neovim.Client
	nvim   *exec.Cmd
	plug   *Example
}

func Test(t *testing.T) { TestingT(t) }

var _ = Suite(&ExampleTest{})

func (t *ExampleTest) SetUpTest(c *C) {
	t.nvim = exec.Command("nvim", "-u", "/dev/null")
	t.nvim.Dir = "/tmp"
	client, err := neovim.NewCmdClient(neovim.NullInitMethod, t.nvim, nil)
	if err != nil {
		log.Fatalf("Could not setup client: %v", errors.Details(err))
	}
	client.PanicOnError = true
	t.client = client
	client.Run()

	plug := &Example{}
	err = plug.Init(t.client, log.New(os.Stderr, "", log.LstdFlags))
	if err != nil {
		log.Fatalf("Could not Init plugin: %v\n", err)
	}
	t.plug = plug
}

func (t *ExampleTest) TearDownTest(c *C) {
	err := t.plug.Shutdown()
	if err != nil {
		log.Fatalf("Could not Shutdown plugin: %v\n", err)
	}
	done := make(chan struct{})
	go func() {
		state, err := t.nvim.Process.Wait()
		if err != nil {
			log.Fatalf("Process did not exit cleanly: %v, %v\n", err, state)
		}
		done <- struct{}{}
	}()
	err = t.client.Close()
	if err != nil {
		log.Fatalf("Could not close client: %v\n", err)
	}
	<-done
}

func (t *ExampleTest) TestGetANumber(c *C) {
	t.client.RegisterSyncRequestHandler("GetANumber", t.plug.newGetANumberResponder)
	topic := "GetANumber"
	commandDef := fmt.Sprintf(`call remote#define#FunctionOnChannel(1, "%v", 1, "%v", {})`, topic, topic)
	_ = t.client.Command(commandDef)
	res, _ := t.client.Eval(`GetANumber()`)
	c.Assert(res, Equals, int64(42))
}
<|endoftext|>


<commit_before>package gcm

import (
	"github.com/alexjlockwood/gcm"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server"
	"github.com/smancke/guble/store"

	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"
	//"runtime"
	"runtime"
)

// GCM_REGISTRATIONS_SCHEMA is the default sqlite schema for gcm
const GCM_REGISTRATIONS_SCHEMA = "gcm_registration"

// GCMConnector is the structure for handling the communication with Google Cloud Messaging
type GCMConnector struct {
	router             server.Router
	kvStore            store.KVStore
	prefix             string
	channelFromRouter  chan server.MsgAndRoute
	closeRouteByRouter chan server.Route
	stopChan           chan bool
	sender             *gcm.Sender
	workersNumber      int
	waitGroup          sync.WaitGroup
}

// NewGCMConnector creates a new gcmConnector without starting it
func NewGCMConnector(router server.Router, prefix string, gcmAPIKey string) (*GCMConnector, error) {

	kvStore, err := router.KVStore()
	if err != nil {
		return nil, err
	}

	//TODO Cosmin: check with dev-team the number of GCM workers, below
	gcm := &GCMConnector{
		router:            router,
		kvStore:           kvStore,
		prefix:            prefix,
		channelFromRouter: make(chan server.MsgAndRoute, 1000),
		stopChan:          make(chan bool, 1),
		sender:            &gcm.Sender{ApiKey: gcmAPIKey},
		workersNumber:     runtime.GOMAXPROCS(0),
	}

	return gcm, nil
}

// Start opens the connector, start more goroutines / workers to handle messages coming from the router
func (conn *GCMConnector) Start() error {
	broadcastRoute := server.NewRoute(removeTrailingSlash(conn.prefix)+"/broadcast", conn.channelFromRouter, "gcm_connector", "gcm_connector")
	conn.router.Subscribe(broadcastRoute)
	go func() {
		//TODO Cosmin: should loadSubscriptions() be taken out of this goroutine, and executed before ?
		// even if startup-time is longer, the routes are guaranteed to be there right after Start() returns
		conn.loadSubscriptions()

		protocol.Debug("number of GCM workers: %v", conn.workersNumber)
		for i := 1; i <= conn.workersNumber; i++ {
			protocol.Debug("starting GCM worker %v", i)
			go conn.loopSendOrBroadcastMessage()
		}
	}()
	return nil
}

// Stop signals the closing of GCMConnector
func (conn *GCMConnector) Stop() error {
	protocol.Debug("GCM Stop()")
	close(conn.stopChan)
	conn.waitGroup.Wait()
	return nil
}

// Check returns nil if health-check succeeds, or an error if health-check fails
func (conn *GCMConnector) Check() error {
	return nil
}

// loopSendOrBroadcastMessage awaits in a loop for messages from router to be forwarded to GCM,
// until the stop-channel is closed
func (conn *GCMConnector) loopSendOrBroadcastMessage() {
	defer conn.waitGroup.Done()
	conn.waitGroup.Add(1)
	for {
		select {
		case msg, opened := <-conn.channelFromRouter:
			if opened {
				if string(msg.Message.Path) == removeTrailingSlash(conn.prefix) + "/broadcast" {
					go conn.broadcastMessage(msg)
				} else {
					go conn.sendMessage(msg)
				}
			}
		case <-conn.stopChan:
			protocol.Debug("stopping GCM worker")
			return
		}
	}
}

func (conn *GCMConnector) sendMessage(msg server.MsgAndRoute) {
	gcmID := msg.Route.ApplicationID

	payload := conn.parseMessageToMap(msg.Message)

	var messageToGcm = gcm.NewMessage(payload, gcmID)
	protocol.Info("sending message to %v ...", gcmID)
	result, err := conn.sender.Send(messageToGcm, 5)
	if err != nil {
		protocol.Err("error sending message to GCM gcmID=%v: %v", gcmID, err.Error())
		return
	}

	errorJSON := result.Results[0].Error
	if errorJSON != "" {
		conn.handleJSONError(errorJSON, gcmID, msg.Route)
	} else {
		protocol.Debug("delivered message to GCM gcmID=%v: %v", gcmID, errorJSON)
	}

	// we only send to one receiver,
	// so we know that we can replace the old id with the first registration id (=canonical id)
	if result.CanonicalIDs != 0 {
		conn.replaceSubscriptionWithCanonicalID(msg.Route, result.Results[0].RegistrationID)
	}
}

func (conn *GCMConnector) parseMessageToMap(msg *protocol.Message) map[string]interface{} {
	payload := map[string]interface{}{}
	if msg.Body[0] == '{' {
		json.Unmarshal(msg.Body, &payload)
	} else {
		payload["message"] = msg.BodyAsString()
	}
	protocol.Debug("parsed message is: %v", payload)
	return payload
}

func (conn *GCMConnector) broadcastMessage(msg server.MsgAndRoute) {
	topic := msg.Message.Path
	payload := conn.parseMessageToMap(msg.Message)
	protocol.Info("broadcasting message with topic %v ...", string(topic))

	subscriptions := conn.kvStore.Iterate(GCM_REGISTRATIONS_SCHEMA, "")
	count := 0
	for {
		select {
		case entry, ok := <-subscriptions:
			if !ok {
				protocol.Info("send message to %v receivers", count)
				return
			}
			gmcID := entry[0]
			//TODO collect 1000 gcmIds and send them in one request!
			broadcastMessage := gcm.NewMessage(payload, gmcID)
			go func() {
				//TODO error handling of response!
				_, err := conn.sender.Send(broadcastMessage, 3)
				protocol.Debug("sent broadcast message to gcmID=%v", gmcID)
				if err != nil {
					protocol.Err("error sending broadcast message to gcmID=%v: %v", gmcID, err.Error())
				}
			}()
			count++
		}
	}
}

func (conn *GCMConnector) replaceSubscriptionWithCanonicalID(route *server.Route, newGcmID string) {
	oldGcmID := route.ApplicationID
	topic := string(route.Path)
	userID := route.UserID

	protocol.Info("replacing old gcmID %v with canonicalId %v", oldGcmID, newGcmID)

	conn.removeSubscription(route, oldGcmID)
	conn.subscribe(topic, userID, newGcmID)
}

func (conn *GCMConnector) handleJSONError(jsonError string, gcmID string, route *server.Route) {
	if jsonError == "NotRegistered" {
		protocol.Debug("remove not registered GCM registration gcmID=%v", gcmID)
		conn.removeSubscription(route, gcmID)
	} else if jsonError == "InvalidRegistration" {
		protocol.Err("the gcmID=%v is not registered. %v", gcmID, jsonError)
	} else {
		protocol.Err("unexpected error while sending to GCM gcmID=%v: %v", gcmID, jsonError)
	}
}

// GetPrefix is used to satisfy the HTTP handler interface
func (conn *GCMConnector) GetPrefix() string {
	return conn.prefix
}

func (conn *GCMConnector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		protocol.Err("Only HTTP POST METHOD SUPPORTED but received type=[%s]", r.Method)
		http.Error(w, "Permission Denied", http.StatusMethodNotAllowed)
		return
	}

	userID, gcmID, topic, err := conn.parseParams(r.URL.Path)
	if err != nil {
		http.Error(w, "Invalid Parameters in request", http.StatusBadRequest)
		return
	}
	conn.subscribe(topic, userID, gcmID)

	fmt.Fprintf(w, "registered: %v\n", topic)
}

// parseParams will parse the HTTP URL with format /gcm/:userid/:gcmid/subscribe/*topic
// returning the parsed Params, or error if the request is not in the correct format
func (conn *GCMConnector) parseParams(path string) (userID, gcmID, topic string, err error) {
	subscribePrefixPath := "subscribe"
	currentURLPath := removeTrailingSlash(path)

	if strings.HasPrefix(currentURLPath, conn.prefix) != true {
		err = errors.New("GCM request is not starting with gcm prefix")
		return
	}
	pathAfterPrefix := strings.TrimPrefix(currentURLPath, conn.prefix)

	splitParams := strings.SplitN(pathAfterPrefix, "/", 3)
	if len(splitParams) != 3 {
		err = errors.New("GCM request has wrong number of params")
		return
	}
	userID = splitParams[0]
	gcmID = splitParams[1]

	if strings.HasPrefix(splitParams[2], subscribePrefixPath+"/") != true {
		err = errors.New("GCM request third param is not subscribe")
		return
	}
	topic = strings.TrimPrefix(splitParams[2], subscribePrefixPath)
	return userID, gcmID, topic, nil
}

func (conn *GCMConnector) subscribe(topic string, userID string, gcmID string) {
	protocol.Info("GCM connector registration to userID=%q, gcmID=%q: %q", userID, gcmID, topic)

	route := server.NewRoute(topic, conn.channelFromRouter, gcmID, userID)

	conn.router.Subscribe(route)
	conn.saveSubscription(userID, topic, gcmID)
}

func (conn *GCMConnector) removeSubscription(route *server.Route, gcmID string) {
	conn.router.Unsubscribe(route)
	conn.kvStore.Delete(GCM_REGISTRATIONS_SCHEMA, gcmID)
}

func (conn *GCMConnector) saveSubscription(userID, topic, gcmID string) {
	conn.kvStore.Put(GCM_REGISTRATIONS_SCHEMA, gcmID, []byte(userID+":"+topic))
}

func (conn *GCMConnector) loadSubscriptions() {
	subscriptions := conn.kvStore.Iterate(GCM_REGISTRATIONS_SCHEMA, "")
	count := 0
	for {
		select {
		case entry, ok := <-subscriptions:
			if !ok {
				protocol.Info("renewed %v GCM subscriptions", count)
				return
			}
			gcmID := entry[0]
			splitValue := strings.SplitN(entry[1], ":", 2)
			userID := splitValue[0]
			topic := splitValue[1]

			protocol.Debug("renewing GCM subscription: userID=%v, topic=%v, gcmID=%v", userID, topic, gcmID)
			route := server.NewRoute(topic, conn.channelFromRouter, gcmID, userID)
			conn.router.Subscribe(route)
			count++
		}
	}
}

func removeTrailingSlash(path string) string {
	if len(path) > 1 && path[len(path)-1] == '/' {
		return path[:len(path)-1]
	}
	return path
}
<commit_msg>better logging of GCM workers<commit_after>package gcm

import (
	"github.com/alexjlockwood/gcm"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server"
	"github.com/smancke/guble/store"

	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"
	//"runtime"
	"runtime"
)

// GCM_REGISTRATIONS_SCHEMA is the default sqlite schema for gcm
const GCM_REGISTRATIONS_SCHEMA = "gcm_registration"

// GCMConnector is the structure for handling the communication with Google Cloud Messaging
type GCMConnector struct {
	router             server.Router
	kvStore            store.KVStore
	prefix             string
	channelFromRouter  chan server.MsgAndRoute
	closeRouteByRouter chan server.Route
	stopChan           chan bool
	sender             *gcm.Sender
	workersNumber      int
	waitGroup          sync.WaitGroup
}

// NewGCMConnector creates a new gcmConnector without starting it
func NewGCMConnector(router server.Router, prefix string, gcmAPIKey string) (*GCMConnector, error) {

	kvStore, err := router.KVStore()
	if err != nil {
		return nil, err
	}

	//TODO Cosmin: check with dev-team the number of GCM workers, below
	gcm := &GCMConnector{
		router:            router,
		kvStore:           kvStore,
		prefix:            prefix,
		channelFromRouter: make(chan server.MsgAndRoute, 1000),
		stopChan:          make(chan bool, 1),
		sender:            &gcm.Sender{ApiKey: gcmAPIKey},
		workersNumber:     runtime.GOMAXPROCS(0),
	}

	return gcm, nil
}

// Start opens the connector, start more goroutines / workers to handle messages coming from the router
func (conn *GCMConnector) Start() error {
	broadcastRoute := server.NewRoute(removeTrailingSlash(conn.prefix)+"/broadcast", conn.channelFromRouter, "gcm_connector", "gcm_connector")
	conn.router.Subscribe(broadcastRoute)
	go func() {
		//TODO Cosmin: should loadSubscriptions() be taken out of this goroutine, and executed before ?
		// even if startup-time is longer, the routes are guaranteed to be there right after Start() returns
		conn.loadSubscriptions()

		protocol.Debug("number of GCM workers: %v", conn.workersNumber)
		for i := 1; i <= conn.workersNumber; i++ {
			go conn.loopSendOrBroadcastMessage(i)
		}
	}()
	return nil
}

// Stop signals the closing of GCMConnector
func (conn *GCMConnector) Stop() error {
	protocol.Debug("GCM Stop()")
	close(conn.stopChan)
	conn.waitGroup.Wait()
	return nil
}

// Check returns nil if health-check succeeds, or an error if health-check fails
func (conn *GCMConnector) Check() error {
	return nil
}

// loopSendOrBroadcastMessage awaits in a loop for messages from router to be forwarded to GCM,
// until the stop-channel is closed
func (conn *GCMConnector) loopSendOrBroadcastMessage(i int) {
	defer conn.waitGroup.Done()
	conn.waitGroup.Add(1)
	protocol.Debug("starting GCM worker %v", i)
	for {
		select {
		case msg, opened := <-conn.channelFromRouter:
			if opened {
				if string(msg.Message.Path) == removeTrailingSlash(conn.prefix) + "/broadcast" {
					go conn.broadcastMessage(msg)
				} else {
					go conn.sendMessage(msg)
				}
			}
		case <-conn.stopChan:
			protocol.Debug("stopping GCM worker %v", i)
			return
		}
	}
}

func (conn *GCMConnector) sendMessage(msg server.MsgAndRoute) {
	gcmID := msg.Route.ApplicationID

	payload := conn.parseMessageToMap(msg.Message)

	var messageToGcm = gcm.NewMessage(payload, gcmID)
	protocol.Info("sending message to %v ...", gcmID)
	result, err := conn.sender.Send(messageToGcm, 5)
	if err != nil {
		protocol.Err("error sending message to GCM gcmID=%v: %v", gcmID, err.Error())
		return
	}

	errorJSON := result.Results[0].Error
	if errorJSON != "" {
		conn.handleJSONError(errorJSON, gcmID, msg.Route)
	} else {
		protocol.Debug("delivered message to GCM gcmID=%v: %v", gcmID, errorJSON)
	}

	// we only send to one receiver,
	// so we know that we can replace the old id with the first registration id (=canonical id)
	if result.CanonicalIDs != 0 {
		conn.replaceSubscriptionWithCanonicalID(msg.Route, result.Results[0].RegistrationID)
	}
}

func (conn *GCMConnector) parseMessageToMap(msg *protocol.Message) map[string]interface{} {
	payload := map[string]interface{}{}
	if msg.Body[0] == '{' {
		json.Unmarshal(msg.Body, &payload)
	} else {
		payload["message"] = msg.BodyAsString()
	}
	protocol.Debug("parsed message is: %v", payload)
	return payload
}

func (conn *GCMConnector) broadcastMessage(msg server.MsgAndRoute) {
	topic := msg.Message.Path
	payload := conn.parseMessageToMap(msg.Message)
	protocol.Info("broadcasting message with topic %v ...", string(topic))

	subscriptions := conn.kvStore.Iterate(GCM_REGISTRATIONS_SCHEMA, "")
	count := 0
	for {
		select {
		case entry, ok := <-subscriptions:
			if !ok {
				protocol.Info("send message to %v receivers", count)
				return
			}
			gmcID := entry[0]
			//TODO collect 1000 gcmIds and send them in one request!
			broadcastMessage := gcm.NewMessage(payload, gmcID)
			go func() {
				//TODO error handling of response!
				_, err := conn.sender.Send(broadcastMessage, 3)
				protocol.Debug("sent broadcast message to gcmID=%v", gmcID)
				if err != nil {
					protocol.Err("error sending broadcast message to gcmID=%v: %v", gmcID, err.Error())
				}
			}()
			count++
		}
	}
}

func (conn *GCMConnector) replaceSubscriptionWithCanonicalID(route *server.Route, newGcmID string) {
	oldGcmID := route.ApplicationID
	topic := string(route.Path)
	userID := route.UserID

	protocol.Info("replacing old gcmID %v with canonicalId %v", oldGcmID, newGcmID)

	conn.removeSubscription(route, oldGcmID)
	conn.subscribe(topic, userID, newGcmID)
}

func (conn *GCMConnector) handleJSONError(jsonError string, gcmID string, route *server.Route) {
	if jsonError == "NotRegistered" {
		protocol.Debug("remove not registered GCM registration gcmID=%v", gcmID)
		conn.removeSubscription(route, gcmID)
	} else if jsonError == "InvalidRegistration" {
		protocol.Err("the gcmID=%v is not registered. %v", gcmID, jsonError)
	} else {
		protocol.Err("unexpected error while sending to GCM gcmID=%v: %v", gcmID, jsonError)
	}
}

// GetPrefix is used to satisfy the HTTP handler interface
func (conn *GCMConnector) GetPrefix() string {
	return conn.prefix
}

func (conn *GCMConnector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		protocol.Err("Only HTTP POST METHOD SUPPORTED but received type=[%s]", r.Method)
		http.Error(w, "Permission Denied", http.StatusMethodNotAllowed)
		return
	}

	userID, gcmID, topic, err := conn.parseParams(r.URL.Path)
	if err != nil {
		http.Error(w, "Invalid Parameters in request", http.StatusBadRequest)
		return
	}
	conn.subscribe(topic, userID, gcmID)

	fmt.Fprintf(w, "registered: %v\n", topic)
}

// parseParams will parse the HTTP URL with format /gcm/:userid/:gcmid/subscribe/*topic
// returning the parsed Params, or error if the request is not in the correct format
func (conn *GCMConnector) parseParams(path string) (userID, gcmID, topic string, err error) {
	subscribePrefixPath := "subscribe"
	currentURLPath := removeTrailingSlash(path)

	if strings.HasPrefix(currentURLPath, conn.prefix) != true {
		err = errors.New("GCM request is not starting with gcm prefix")
		return
	}
	pathAfterPrefix := strings.TrimPrefix(currentURLPath, conn.prefix)

	splitParams := strings.SplitN(pathAfterPrefix, "/", 3)
	if len(splitParams) != 3 {
		err = errors.New("GCM request has wrong number of params")
		return
	}
	userID = splitParams[0]
	gcmID = splitParams[1]

	if strings.HasPrefix(splitParams[2], subscribePrefixPath+"/") != true {
		err = errors.New("GCM request third param is not subscribe")
		return
	}
	topic = strings.TrimPrefix(splitParams[2], subscribePrefixPath)
	return userID, gcmID, topic, nil
}

func (conn *GCMConnector) subscribe(topic string, userID string, gcmID string) {
	protocol.Info("GCM connector registration to userID=%q, gcmID=%q: %q", userID, gcmID, topic)

	route := server.NewRoute(topic, conn.channelFromRouter, gcmID, userID)

	conn.router.Subscribe(route)
	conn.saveSubscription(userID, topic, gcmID)
}

func (conn *GCMConnector) removeSubscription(route *server.Route, gcmID string) {
	conn.router.Unsubscribe(route)
	conn.kvStore.Delete(GCM_REGISTRATIONS_SCHEMA, gcmID)
}

func (conn *GCMConnector) saveSubscription(userID, topic, gcmID string) {
	conn.kvStore.Put(GCM_REGISTRATIONS_SCHEMA, gcmID, []byte(userID+":"+topic))
}

func (conn *GCMConnector) loadSubscriptions() {
	subscriptions := conn.kvStore.Iterate(GCM_REGISTRATIONS_SCHEMA, "")
	count := 0
	for {
		select {
		case entry, ok := <-subscriptions:
			if !ok {
				protocol.Info("renewed %v GCM subscriptions", count)
				return
			}
			gcmID := entry[0]
			splitValue := strings.SplitN(entry[1], ":", 2)
			userID := splitValue[0]
			topic := splitValue[1]

			protocol.Debug("renewing GCM subscription: userID=%v, topic=%v, gcmID=%v", userID, topic, gcmID)
			route := server.NewRoute(topic, conn.channelFromRouter, gcmID, userID)
			conn.router.Subscribe(route)
			count++
		}
	}
}

func removeTrailingSlash(path string) string {
	if len(path) > 1 && path[len(path)-1] == '/' {
		return path[:len(path)-1]
	}
	return path
}
<|endoftext|>