<commit_before>// Copyright 2014 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 tar contains helper functions for working with tar files
package tar

import (
	"archive/tar"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
)

const DEFAULT_DIR_MODE os.FileMode = 0755

type insecureLinkError error

// Map of paths that should be whitelisted. The paths should be relative to the
// root of the tar file and should be cleaned (for example using filepath.Clean)
type PathWhitelistMap map[string]struct{}

// ExtractTar extracts a tarball (from a tar.Reader) into the given directory
// if pwl is not nil, only the paths in the map are extracted.
// If overwrite is true, existing files will be overwritten.
func ExtractTar(tr *tar.Reader, dir string, overwrite bool, pwl PathWhitelistMap) error {
	um := syscall.Umask(0)
	defer syscall.Umask(um)
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			return nil
		case nil:
			if pwl != nil {
				relpath := filepath.Clean(hdr.Name)
				if _, ok := pwl[relpath]; !ok {
					continue
				}
			}
			err = ExtractFile(tr, hdr, dir, overwrite)
			if err != nil {
				return fmt.Errorf("error extracting tarball: %v", err)
			}
		default:
			return fmt.Errorf("error extracting tarball: %v", err)
		}
	}
}

// ExtractFile extracts the file described by hdr from the given tarball into
// the provided directory.
// If overwrite is true, existing files will be overwritten.
func ExtractFile(tr *tar.Reader, hdr *tar.Header, dir string, overwrite bool) error {
	p := filepath.Join(dir, hdr.Name)
	fi := hdr.FileInfo()
	typ := hdr.Typeflag
	if overwrite {
		info, err := os.Lstat(p)
		switch {
		case os.IsNotExist(err):
		case err == nil:
			// If the old and new paths are both dirs do nothing or
			// RemoveAll will remove all dir's contents
			if !info.IsDir() || typ != tar.TypeDir {
				err := os.RemoveAll(p)
				if err != nil {
					return err
				}
			}
		default:
			return err
		}
	}

	// Create parent dir if it doesn't exist
	if err := os.MkdirAll(filepath.Dir(p), DEFAULT_DIR_MODE); err != nil {
		return err
	}
	switch {
	case typ == tar.TypeReg || typ == tar.TypeRegA:
		f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, fi.Mode())
		if err != nil {
			return err
		}
		_, err = io.Copy(f, tr)
		if err != nil {
			f.Close()
			return err
		}
		f.Close()
	case typ == tar.TypeDir:
		if err := os.MkdirAll(p, fi.Mode()); err != nil {
			return err
		}
		dir, err := os.Open(p)
		if err != nil {
			return err
		}
		if err := dir.Chmod(fi.Mode()); err != nil {
			dir.Close()
			return err
		}
		dir.Close()
	case typ == tar.TypeLink:
		dest := filepath.Join(dir, hdr.Linkname)
		if !strings.HasPrefix(dest, dir) {
			return insecureLinkError(fmt.Errorf("insecure link %q -> %q", p, hdr.Linkname))
		}
		if err := os.Link(dest, p); err != nil {
			return err
		}
	case typ == tar.TypeSymlink:
		dest := filepath.Join(filepath.Dir(p), hdr.Linkname)
		if !strings.HasPrefix(dest, dir) {
			return insecureLinkError(fmt.Errorf("insecure symlink %q -> %q", p, hdr.Linkname))
		}
		if err := os.Symlink(hdr.Linkname, p); err != nil {
			return err
		}
	case typ == tar.TypeChar:
		dev := makedev(int(hdr.Devmajor), int(hdr.Devminor))
		mode := uint32(fi.Mode()) | syscall.S_IFCHR
		if err := syscall.Mknod(p, mode, dev); err != nil {
			return err
		}
	case typ == tar.TypeBlock:
		dev := makedev(int(hdr.Devmajor), int(hdr.Devminor))
		mode := uint32(fi.Mode()) | syscall.S_IFBLK
		if err := syscall.Mknod(p, mode, dev); err != nil {
			return err
		}
	// TODO(jonboulle): implement other modes
	default:
		return fmt.Errorf("unsupported type: %v", typ)
	}

	if err := os.Lchown(p, hdr.Uid, hdr.Gid); err != nil {
		return err
	}

	// lchown(2) says that, depending on the linux kernel version, it
	// can change the file's mode also if executed as root. So call
	// os.Chmod after it.
	if typ != tar.TypeSymlink {
		if err := os.Chmod(p, fi.Mode()); err != nil {
			return err
		}
	}

	return nil
}

// ExtractFileFromTar extracts a regular file from the given tar, returning its
// contents as a byte slice
func ExtractFileFromTar(tr *tar.Reader, file string) ([]byte, error) {
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			return nil, fmt.Errorf("file not found")
		case nil:
			if filepath.Clean(hdr.Name) != filepath.Clean(file) {
				continue
			}
			switch hdr.Typeflag {
			case tar.TypeReg:
			case tar.TypeRegA:
			default:
				return nil, fmt.Errorf("requested file not a regular file")
			}
			buf, err := ioutil.ReadAll(tr)
			if err != nil {
				return nil, fmt.Errorf("error extracting tarball: %v", err)
			}
			return buf, nil
		default:
			return nil, fmt.Errorf("error extracting tarball: %v", err)
		}
	}
}

// makedev mimics glib's gnu_dev_makedev
func makedev(major, minor int) int {
	return (minor & 0xff) | (major & 0xfff << 8) | int((uint64(minor & ^0xff) << 12)) | int(uint64(major & ^0xfff)<<32)
}
<commit_msg>pkg/tar: implement tar.TypeFifo<commit_after>// Copyright 2014 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 tar contains helper functions for working with tar files
package tar

import (
	"archive/tar"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
)

const DEFAULT_DIR_MODE os.FileMode = 0755

type insecureLinkError error

// Map of paths that should be whitelisted. The paths should be relative to the
// root of the tar file and should be cleaned (for example using filepath.Clean)
type PathWhitelistMap map[string]struct{}

// ExtractTar extracts a tarball (from a tar.Reader) into the given directory
// if pwl is not nil, only the paths in the map are extracted.
// If overwrite is true, existing files will be overwritten.
func ExtractTar(tr *tar.Reader, dir string, overwrite bool, pwl PathWhitelistMap) error {
	um := syscall.Umask(0)
	defer syscall.Umask(um)
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			return nil
		case nil:
			if pwl != nil {
				relpath := filepath.Clean(hdr.Name)
				if _, ok := pwl[relpath]; !ok {
					continue
				}
			}
			err = ExtractFile(tr, hdr, dir, overwrite)
			if err != nil {
				return fmt.Errorf("error extracting tarball: %v", err)
			}
		default:
			return fmt.Errorf("error extracting tarball: %v", err)
		}
	}
}

// ExtractFile extracts the file described by hdr from the given tarball into
// the provided directory.
// If overwrite is true, existing files will be overwritten.
func ExtractFile(tr *tar.Reader, hdr *tar.Header, dir string, overwrite bool) error {
	p := filepath.Join(dir, hdr.Name)
	fi := hdr.FileInfo()
	typ := hdr.Typeflag
	if overwrite {
		info, err := os.Lstat(p)
		switch {
		case os.IsNotExist(err):
		case err == nil:
			// If the old and new paths are both dirs do nothing or
			// RemoveAll will remove all dir's contents
			if !info.IsDir() || typ != tar.TypeDir {
				err := os.RemoveAll(p)
				if err != nil {
					return err
				}
			}
		default:
			return err
		}
	}

	// Create parent dir if it doesn't exist
	if err := os.MkdirAll(filepath.Dir(p), DEFAULT_DIR_MODE); err != nil {
		return err
	}
	switch {
	case typ == tar.TypeReg || typ == tar.TypeRegA:
		f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, fi.Mode())
		if err != nil {
			return err
		}
		_, err = io.Copy(f, tr)
		if err != nil {
			f.Close()
			return err
		}
		f.Close()
	case typ == tar.TypeDir:
		if err := os.MkdirAll(p, fi.Mode()); err != nil {
			return err
		}
		dir, err := os.Open(p)
		if err != nil {
			return err
		}
		if err := dir.Chmod(fi.Mode()); err != nil {
			dir.Close()
			return err
		}
		dir.Close()
	case typ == tar.TypeLink:
		dest := filepath.Join(dir, hdr.Linkname)
		if !strings.HasPrefix(dest, dir) {
			return insecureLinkError(fmt.Errorf("insecure link %q -> %q", p, hdr.Linkname))
		}
		if err := os.Link(dest, p); err != nil {
			return err
		}
	case typ == tar.TypeSymlink:
		dest := filepath.Join(filepath.Dir(p), hdr.Linkname)
		if !strings.HasPrefix(dest, dir) {
			return insecureLinkError(fmt.Errorf("insecure symlink %q -> %q", p, hdr.Linkname))
		}
		if err := os.Symlink(hdr.Linkname, p); err != nil {
			return err
		}
	case typ == tar.TypeChar:
		dev := makedev(int(hdr.Devmajor), int(hdr.Devminor))
		mode := uint32(fi.Mode()) | syscall.S_IFCHR
		if err := syscall.Mknod(p, mode, dev); err != nil {
			return err
		}
	case typ == tar.TypeBlock:
		dev := makedev(int(hdr.Devmajor), int(hdr.Devminor))
		mode := uint32(fi.Mode()) | syscall.S_IFBLK
		if err := syscall.Mknod(p, mode, dev); err != nil {
			return err
		}
	case typ == tar.TypeFifo:
		if err := syscall.Mkfifo(p, uint32(fi.Mode())); err != nil {
			return err
		}
	// TODO(jonboulle): implement other modes
	default:
		return fmt.Errorf("unsupported type: %v", typ)
	}

	if err := os.Lchown(p, hdr.Uid, hdr.Gid); err != nil {
		return err
	}

	// lchown(2) says that, depending on the linux kernel version, it
	// can change the file's mode also if executed as root. So call
	// os.Chmod after it.
	if typ != tar.TypeSymlink {
		if err := os.Chmod(p, fi.Mode()); err != nil {
			return err
		}
	}

	return nil
}

// ExtractFileFromTar extracts a regular file from the given tar, returning its
// contents as a byte slice
func ExtractFileFromTar(tr *tar.Reader, file string) ([]byte, error) {
	for {
		hdr, err := tr.Next()
		switch err {
		case io.EOF:
			return nil, fmt.Errorf("file not found")
		case nil:
			if filepath.Clean(hdr.Name) != filepath.Clean(file) {
				continue
			}
			switch hdr.Typeflag {
			case tar.TypeReg:
			case tar.TypeRegA:
			default:
				return nil, fmt.Errorf("requested file not a regular file")
			}
			buf, err := ioutil.ReadAll(tr)
			if err != nil {
				return nil, fmt.Errorf("error extracting tarball: %v", err)
			}
			return buf, nil
		default:
			return nil, fmt.Errorf("error extracting tarball: %v", err)
		}
	}
}

// makedev mimics glib's gnu_dev_makedev
func makedev(major, minor int) int {
	return (minor & 0xff) | (major & 0xfff << 8) | int((uint64(minor & ^0xff) << 12)) | int(uint64(major & ^0xfff)<<32)
}
<|endoftext|>


<commit_before>/* Copyright 2017 LinkedIn Corp. 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.
 */

package notifier

import (
	"encoding/json"
	"fmt"
	"net/http"
	"text/template"
	"time"

	"github.com/stretchr/testify/assert"
	"net/http/httptest"
	"testing"

	"github.com/spf13/viper"
	"go.uber.org/zap"

	"github.com/linkedin/Burrow/core/protocol"
)

func fixtureHTTPNotifier() *HTTPNotifier {
	module := HTTPNotifier{
		Log: zap.NewNop(),
	}
	module.App = &protocol.ApplicationContext{}

	viper.Reset()
	viper.Set("notifier.test.class-name", "http")
	viper.Set("notifier.test.url-open", "url_open")
	viper.Set("notifier.test.url-close", "url_close")
	viper.Set("notifier.test.template-open", "template_open")
	viper.Set("notifier.test.template-close", "template_close")
	viper.Set("notifier.test.send-close", false)
	viper.Set("notifier.test.headers", map[string]string{"Token": "testtoken"})

	return &module
}

func TestHttpNotifier_ImplementsModule(t *testing.T) {
	assert.Implements(t, (*protocol.Module)(nil), new(HTTPNotifier))
	assert.Implements(t, (*Module)(nil), new(HTTPNotifier))
}

func TestHttpNotifier_Configure(t *testing.T) {
	module := fixtureHTTPNotifier()

	module.Configure("test", "notifier.test")
	assert.NotNil(t, module.httpClient, "Expected httpClient to be set with a client object")
}

func TestHttpNotifier_StartStop(t *testing.T) {
	module := fixtureHTTPNotifier()
	module.Configure("test", "notifier.test")

	err := module.Start()
	assert.Nil(t, err, "Expected Start to return no error")
	err = module.Stop()
	assert.Nil(t, err, "Expected Stop to return no error")
}

func TestHttpNotifier_AcceptConsumerGroup(t *testing.T) {
	module := fixtureHTTPNotifier()
	module.Configure("test", "notifier.test")

	// Should always return true
	assert.True(t, module.AcceptConsumerGroup(&protocol.ConsumerGroupStatus{}), "Expected any status to return True")
}

// Struct that will be used for sending HTTP requests for testing
type HTTPRequest struct {
	Template string
	ID       string
	Cluster  string
	Group    string
}

func TestHttpNotifier_Notify_Open(t *testing.T) {
	// handler that validates that we get the right values
	requestHandler := func(w http.ResponseWriter, r *http.Request) {
		// Must get an appropriate Content-Type header
		headers, ok := r.Header["Content-Type"]
		assert.True(t, ok, "Expected to receive Content-Type header")
		assert.Len(t, headers, 1, "Expected to receive exactly one Content-Type header")
		assert.Equalf(t, "application/json", headers[0], "Expected Content-Type header to be 'application/json', not '%v'", headers[0])

		tokenHeaders, ok := r.Header["Token"]
		assert.True(t, ok, "Expected to receive Token header")
		assert.Equalf(t, "testtoken", tokenHeaders[0], "Expected Token header to be 'testtoken', not '%v'", tokenHeaders[0])

		decoder := json.NewDecoder(r.Body)
		var req HTTPRequest
		err := decoder.Decode(&req)
		if err != nil {
			assert.Failf(t, "Failed to decode message body", "Failed to decode message body: %v", err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		assert.Equalf(t, "template_open", req.Template, "Expected Template to be template_open, not %v", req.Template)
		assert.Equalf(t, "testidstring", req.ID, "Expected ID to be testidstring, not %v", req.ID)
		assert.Equalf(t, "testcluster", req.Cluster, "Expected Cluster to be testcluster, not %v", req.Cluster)
		assert.Equalf(t, "testgroup", req.Group, "Expected Group to be testgroup, not %v", req.Group)

		fmt.Fprint(w, "ok")
	}

	// create test server with handler
	ts := httptest.NewServer(http.HandlerFunc(requestHandler))
	defer ts.Close()

	module := fixtureHTTPNotifier()
	viper.Set("notifier.test.url-open", ts.URL)

	// Template sends the ID, cluster, and group
	module.templateOpen, _ = template.New("test").Parse("{\"template\":\"template_open\",\"id\":\"{{.ID}}\",\"cluster\":\"{{.Cluster}}\",\"group\":\"{{.Group}}\"}")

	module.Configure("test", "notifier.test")

	status := &protocol.ConsumerGroupStatus{
		Status:  protocol.StatusWarning,
		Cluster: "testcluster",
		Group:   "testgroup",
	}

	module.Notify(status, "testidstring", time.Now(), false)
}

func TestHttpNotifier_Notify_Close(t *testing.T) {
	// handler that validates that we get the right values
	requestHandler := func(w http.ResponseWriter, r *http.Request) {
		// Must get an appropriate Content-Type header
		headers, ok := r.Header["Content-Type"]
		assert.True(t, ok, "Expected to receive Content-Type header")
		assert.Len(t, headers, 1, "Expected to receive exactly one Content-Type header")
		assert.Equalf(t, "application/json", headers[0], "Expected Content-Type header to be 'application/json', not '%v'", headers[0])

		tokenHeaders, ok := r.Header["Token"]
		assert.True(t, ok, "Expected to receive Token header")
		assert.Equalf(t, "testtoken", tokenHeaders[0], "Expected Token header to be 'testtoken', not '%v'", tokenHeaders[0])

		decoder := json.NewDecoder(r.Body)
		var req HTTPRequest
		err := decoder.Decode(&req)
		if err != nil {
			assert.Failf(t, "Failed to decode message body", "Failed to decode message body: %v", err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		assert.Equalf(t, "template_close", req.Template, "Expected Template to be template_close, not %v", req.Template)
		assert.Equalf(t, "testidstring", req.ID, "Expected ID to be testidstring, not %v", req.ID)
		assert.Equalf(t, "testcluster", req.Cluster, "Expected Cluster to be testcluster, not %v", req.Cluster)
		assert.Equalf(t, "testgroup", req.Group, "Expected Group to be testgroup, not %v", req.Group)

		fmt.Fprint(w, "ok")
	}

	// create test server with handler
	ts := httptest.NewServer(http.HandlerFunc(requestHandler))
	defer ts.Close()

	module := fixtureHTTPNotifier()
	viper.Set("notifier.test.send-close", true)
	viper.Set("notifier.test.url-close", ts.URL)

	// Template sends the ID, cluster, and group
	module.templateClose, _ = template.New("test").Parse("{\"template\":\"template_close\",\"id\":\"{{.ID}}\",\"cluster\":\"{{.Cluster}}\",\"group\":\"{{.Group}}\"}")

	module.Configure("test", "notifier.test")

	status := &protocol.ConsumerGroupStatus{
		Status:  protocol.StatusWarning,
		Cluster: "testcluster",
		Group:   "testgroup",
	}

	module.Notify(status, "testidstring", time.Now(), true)
}
<commit_msg>add url-* template test<commit_after>/* Copyright 2017 LinkedIn Corp. 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.
 */

package notifier

import (
	"encoding/json"
	"fmt"
	"net/http"
	"text/template"
	"time"

	"github.com/stretchr/testify/assert"
	"net/http/httptest"
	"testing"

	"github.com/spf13/viper"
	"go.uber.org/zap"

	"github.com/linkedin/Burrow/core/protocol"
)

func fixtureHTTPNotifier() *HTTPNotifier {
	module := HTTPNotifier{
		Log: zap.NewNop(),
	}
	module.App = &protocol.ApplicationContext{}

	viper.Reset()
	viper.Set("notifier.test.class-name", "http")
	viper.Set("notifier.test.url-open", "url_open")
	viper.Set("notifier.test.url-close", "url_close")
	viper.Set("notifier.test.template-open", "template_open")
	viper.Set("notifier.test.template-close", "template_close")
	viper.Set("notifier.test.send-close", false)
	viper.Set("notifier.test.headers", map[string]string{"Token": "testtoken"})

	return &module
}

func TestHttpNotifier_ImplementsModule(t *testing.T) {
	assert.Implements(t, (*protocol.Module)(nil), new(HTTPNotifier))
	assert.Implements(t, (*Module)(nil), new(HTTPNotifier))
}

func TestHttpNotifier_Configure(t *testing.T) {
	module := fixtureHTTPNotifier()

	module.Configure("test", "notifier.test")
	assert.NotNil(t, module.httpClient, "Expected httpClient to be set with a client object")
}

func TestHttpNotifier_StartStop(t *testing.T) {
	module := fixtureHTTPNotifier()
	module.Configure("test", "notifier.test")

	err := module.Start()
	assert.Nil(t, err, "Expected Start to return no error")
	err = module.Stop()
	assert.Nil(t, err, "Expected Stop to return no error")
}

func TestHttpNotifier_AcceptConsumerGroup(t *testing.T) {
	module := fixtureHTTPNotifier()
	module.Configure("test", "notifier.test")

	// Should always return true
	assert.True(t, module.AcceptConsumerGroup(&protocol.ConsumerGroupStatus{}), "Expected any status to return True")
}

// Struct that will be used for sending HTTP requests for testing
type HTTPRequest struct {
	Template string
	ID       string
	Cluster  string
	Group    string
}

func TestHttpNotifier_Notify_Open(t *testing.T) {
	// handler that validates that we get the right values
	requestHandler := func(w http.ResponseWriter, r *http.Request) {
		// Must get an appropriate Content-Type header
		headers, ok := r.Header["Content-Type"]
		assert.True(t, ok, "Expected to receive Content-Type header")
		assert.Len(t, headers, 1, "Expected to receive exactly one Content-Type header")
		assert.Equalf(t, "application/json", headers[0], "Expected Content-Type header to be 'application/json', not '%v'", headers[0])

		tokenHeaders, ok := r.Header["Token"]
		assert.True(t, ok, "Expected to receive Token header")
		assert.Equalf(t, "testtoken", tokenHeaders[0], "Expected Token header to be 'testtoken', not '%v'", tokenHeaders[0])

		assert.Equalf(t, "id=testidstring", r.URL.RawQuery, "Expected URL querystring to be id=testidstring, not %v", r.URL)

		decoder := json.NewDecoder(r.Body)
		var req HTTPRequest
		err := decoder.Decode(&req)
		if err != nil {
			assert.Failf(t, "Failed to decode message body", "Failed to decode message body: %v", err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		assert.Equalf(t, "template_open", req.Template, "Expected Template to be template_open, not %v", req.Template)
		assert.Equalf(t, "testidstring", req.ID, "Expected ID to be testidstring, not %v", req.ID)
		assert.Equalf(t, "testcluster", req.Cluster, "Expected Cluster to be testcluster, not %v", req.Cluster)
		assert.Equalf(t, "testgroup", req.Group, "Expected Group to be testgroup, not %v", req.Group)

		fmt.Fprint(w, "ok")
	}

	// create test server with handler
	ts := httptest.NewServer(http.HandlerFunc(requestHandler))
	defer ts.Close()

	module := fixtureHTTPNotifier()
	viper.Set("notifier.test.url-open", fmt.Sprintf("%s?id={{.ID}}", ts.URL))

	// Template sends the ID, cluster, and group
	module.templateOpen, _ = template.New("test").Parse("{\"template\":\"template_open\",\"id\":\"{{.ID}}\",\"cluster\":\"{{.Cluster}}\",\"group\":\"{{.Group}}\"}")

	module.Configure("test", "notifier.test")

	status := &protocol.ConsumerGroupStatus{
		Status:  protocol.StatusWarning,
		Cluster: "testcluster",
		Group:   "testgroup",
	}

	module.Notify(status, "testidstring", time.Now(), false)
}

func TestHttpNotifier_Notify_Close(t *testing.T) {
	// handler that validates that we get the right values
	requestHandler := func(w http.ResponseWriter, r *http.Request) {
		// Must get an appropriate Content-Type header
		headers, ok := r.Header["Content-Type"]
		assert.True(t, ok, "Expected to receive Content-Type header")
		assert.Len(t, headers, 1, "Expected to receive exactly one Content-Type header")
		assert.Equalf(t, "application/json", headers[0], "Expected Content-Type header to be 'application/json', not '%v'", headers[0])

		tokenHeaders, ok := r.Header["Token"]
		assert.True(t, ok, "Expected to receive Token header")
		assert.Equalf(t, "testtoken", tokenHeaders[0], "Expected Token header to be 'testtoken', not '%v'", tokenHeaders[0])

		assert.Equalf(t, "id=testidstring", r.URL.RawQuery, "Expected URL querystring to be id=testidstring, not %v", r.URL)

		decoder := json.NewDecoder(r.Body)
		var req HTTPRequest
		err := decoder.Decode(&req)
		if err != nil {
			assert.Failf(t, "Failed to decode message body", "Failed to decode message body: %v", err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		assert.Equalf(t, "template_close", req.Template, "Expected Template to be template_close, not %v", req.Template)
		assert.Equalf(t, "testidstring", req.ID, "Expected ID to be testidstring, not %v", req.ID)
		assert.Equalf(t, "testcluster", req.Cluster, "Expected Cluster to be testcluster, not %v", req.Cluster)
		assert.Equalf(t, "testgroup", req.Group, "Expected Group to be testgroup, not %v", req.Group)

		fmt.Fprint(w, "ok")
	}

	// create test server with handler
	ts := httptest.NewServer(http.HandlerFunc(requestHandler))
	defer ts.Close()

	module := fixtureHTTPNotifier()
	viper.Set("notifier.test.send-close", true)
	viper.Set("notifier.test.url-close", fmt.Sprintf("%s?id={{.ID}}", ts.URL))

	// Template sends the ID, cluster, and group
	module.templateClose, _ = template.New("test").Parse("{\"template\":\"template_close\",\"id\":\"{{.ID}}\",\"cluster\":\"{{.Cluster}}\",\"group\":\"{{.Group}}\"}")

	module.Configure("test", "notifier.test")

	status := &protocol.ConsumerGroupStatus{
		Status:  protocol.StatusWarning,
		Cluster: "testcluster",
		Group:   "testgroup",
	}

	module.Notify(status, "testidstring", time.Now(), true)
}
<|endoftext|>


<commit_before>package ploop

// #include <ploop/libploop.h>
import "C"
import "fmt"

type PloopErr struct {
	C int
	s string
}

// SYSEXIT_* errors
const (
	_ = iota
	E_CREAT
	E_DEVICE
	E_DEVIOC
	E_OPEN
	E_MALLOC
	E_READ
	E_WRITE
	E_RESERVED_8
	E_SYSFS
	E_RESERVED_10
	E_PLOOPFMT
	E_SYS
	E_PROTOCOL
	E_LOOP
	E_FSTAT
	E_FSYNC
	E_EBUSY
	E_FLOCK
	E_FTRUNCATE
	E_FALLOCATE
	E_MOUNT
	E_UMOUNT
	E_LOCK
	E_MKFS
	E_RESERVED_25
	E_RESIZE_FS
	E_MKDIR
	E_RENAME
	E_ABORT
	E_RELOC
	E_RESERVED_31
	E_RESERVED_32
	E_CHANGE_GPT
	E_RESERVED_34
	E_UNLINK
	E_MKNOD
	E_PLOOPINUSE
	E_PARAM
	E_DISKDESCR
	E_DEV_NOT_MOUNTED
	E_FSCK
	E_RESERVED_42
	E_NOSNAP
)

var ErrCodes = []string{
	E_CREAT:           "E_CREAT",
	E_DEVICE:          "E_DEVICE",
	E_DEVIOC:          "E_DEVIOC",
	E_OPEN:            "E_OPEN",
	E_MALLOC:          "E_MALLOC",
	E_READ:            "E_READ",
	E_WRITE:           "E_WRITE",
	E_RESERVED_8:      "E_RESERVED",
	E_SYSFS:           "E_SYSFS",
	E_RESERVED_10:     "E_RESERVED",
	E_PLOOPFMT:        "E_PLOOPFMT",
	E_SYS:             "E_SYS",
	E_PROTOCOL:        "E_PROTOCOL",
	E_LOOP:            "E_LOOP",
	E_FSTAT:           "E_FSTAT",
	E_FSYNC:           "E_FSYNC",
	E_EBUSY:           "E_EBUSY",
	E_FLOCK:           "E_FLOCK",
	E_FTRUNCATE:       "E_FTRUNCATE",
	E_FALLOCATE:       "E_FALLOCATE",
	E_MOUNT:           "E_MOUNT",
	E_UMOUNT:          "E_UMOUNT",
	E_LOCK:            "E_LOCK",
	E_MKFS:            "E_MKFS",
	E_RESERVED_25:     "E_RESERVED",
	E_RESIZE_FS:       "E_RESIZE_FS",
	E_MKDIR:           "E_MKDIR",
	E_RENAME:          "E_RENAME",
	E_ABORT:           "E_ABORT",
	E_RELOC:           "E_RELOC",
	E_RESERVED_31:     "E_RESERVED",
	E_RESERVED_32:     "E_RESERVED",
	E_CHANGE_GPT:      "E_CHANGE_GPT",
	E_RESERVED_34:     "E_RESERVED",
	E_UNLINK:          "E_UNLINK",
	E_MKNOD:           "E_MKNOD",
	E_PLOOPINUSE:      "E_PLOOPINUSE",
	E_PARAM:           "E_PARAM",
	E_DISKDESCR:       "E_DISKDESCR",
	E_DEV_NOT_MOUNTED: "E_DEV_NOT_MOUNTED",
	E_FSCK:            "E_FSCK",
	E_RESERVED_42:     "E_RESERVED",
	E_NOSNAP:          "E_NOSNAP",
}

// Error returns a string representation of a ploop error
func (e *PloopErr) Error() string {
	s := "E_UNKNOWN"
	if e.C > 0 && e.C < len(ErrCodes) {
		s = ErrCodes[e.C]
	}

	return fmt.Sprintf("ploop error %d (%s): %s", e.C, s, e.s)
}

// IsError checks if a ploop error has specified code
func (e *PloopErr) IsError(code int) bool {
	return e.C == code
}

// IsNotMounted returns true if ploop error is "device is not mounted"
func (e *PloopErr) IsNotMounted() bool {
	return e.IsError(E_DEV_NOT_MOUNTED)
}

func mkerr(ret C.int) error {
	if ret == 0 {
		return nil
	}

	return &PloopErr{C: int(ret), s: C.GoString(C.ploop_get_last_error())}
}
<commit_msg>PloopErr: make error code private<commit_after>package ploop

// #include <ploop/libploop.h>
import "C"
import "fmt"

type PloopErr struct {
	c int
	s string
}

// SYSEXIT_* errors
const (
	_ = iota
	E_CREAT
	E_DEVICE
	E_DEVIOC
	E_OPEN
	E_MALLOC
	E_READ
	E_WRITE
	E_RESERVED_8
	E_SYSFS
	E_RESERVED_10
	E_PLOOPFMT
	E_SYS
	E_PROTOCOL
	E_LOOP
	E_FSTAT
	E_FSYNC
	E_EBUSY
	E_FLOCK
	E_FTRUNCATE
	E_FALLOCATE
	E_MOUNT
	E_UMOUNT
	E_LOCK
	E_MKFS
	E_RESERVED_25
	E_RESIZE_FS
	E_MKDIR
	E_RENAME
	E_ABORT
	E_RELOC
	E_RESERVED_31
	E_RESERVED_32
	E_CHANGE_GPT
	E_RESERVED_34
	E_UNLINK
	E_MKNOD
	E_PLOOPINUSE
	E_PARAM
	E_DISKDESCR
	E_DEV_NOT_MOUNTED
	E_FSCK
	E_RESERVED_42
	E_NOSNAP
)

var ErrCodes = []string{
	E_CREAT:           "E_CREAT",
	E_DEVICE:          "E_DEVICE",
	E_DEVIOC:          "E_DEVIOC",
	E_OPEN:            "E_OPEN",
	E_MALLOC:          "E_MALLOC",
	E_READ:            "E_READ",
	E_WRITE:           "E_WRITE",
	E_RESERVED_8:      "E_RESERVED",
	E_SYSFS:           "E_SYSFS",
	E_RESERVED_10:     "E_RESERVED",
	E_PLOOPFMT:        "E_PLOOPFMT",
	E_SYS:             "E_SYS",
	E_PROTOCOL:        "E_PROTOCOL",
	E_LOOP:            "E_LOOP",
	E_FSTAT:           "E_FSTAT",
	E_FSYNC:           "E_FSYNC",
	E_EBUSY:           "E_EBUSY",
	E_FLOCK:           "E_FLOCK",
	E_FTRUNCATE:       "E_FTRUNCATE",
	E_FALLOCATE:       "E_FALLOCATE",
	E_MOUNT:           "E_MOUNT",
	E_UMOUNT:          "E_UMOUNT",
	E_LOCK:            "E_LOCK",
	E_MKFS:            "E_MKFS",
	E_RESERVED_25:     "E_RESERVED",
	E_RESIZE_FS:       "E_RESIZE_FS",
	E_MKDIR:           "E_MKDIR",
	E_RENAME:          "E_RENAME",
	E_ABORT:           "E_ABORT",
	E_RELOC:           "E_RELOC",
	E_RESERVED_31:     "E_RESERVED",
	E_RESERVED_32:     "E_RESERVED",
	E_CHANGE_GPT:      "E_CHANGE_GPT",
	E_RESERVED_34:     "E_RESERVED",
	E_UNLINK:          "E_UNLINK",
	E_MKNOD:           "E_MKNOD",
	E_PLOOPINUSE:      "E_PLOOPINUSE",
	E_PARAM:           "E_PARAM",
	E_DISKDESCR:       "E_DISKDESCR",
	E_DEV_NOT_MOUNTED: "E_DEV_NOT_MOUNTED",
	E_FSCK:            "E_FSCK",
	E_RESERVED_42:     "E_RESERVED",
	E_NOSNAP:          "E_NOSNAP",
}

// Error returns a string representation of a ploop error
func (e *PloopErr) Error() string {
	s := "E_UNKNOWN"
	if e.c > 0 && e.c < len(ErrCodes) {
		s = ErrCodes[e.c]
	}

	return fmt.Sprintf("ploop error %d (%s): %s", e.c, s, e.s)
}

// IsError checks if a ploop error has specified code
func (e *PloopErr) IsError(code int) bool {
	return e.c == code
}

// IsNotMounted returns true if ploop error is "device is not mounted"
func (e *PloopErr) IsNotMounted() bool {
	return e.IsError(E_DEV_NOT_MOUNTED)
}

func mkerr(ret C.int) error {
	if ret == 0 {
		return nil
	}

	return &PloopErr{c: int(ret), s: C.GoString(C.ploop_get_last_error())}
}
<|endoftext|>


<commit_before>package log

//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg log -path App,Log

import (
	"context"

	"v2ray.com/core/app"
	"v2ray.com/core/app/log/internal"
	"v2ray.com/core/common"
	"v2ray.com/core/common/errors"
)

var (
	streamLoggerInstance internal.LogWriter = internal.NewStdOutLogWriter()

	debugLogger   internal.LogWriter = streamLoggerInstance
	infoLogger    internal.LogWriter = streamLoggerInstance
	warningLogger internal.LogWriter = streamLoggerInstance
	errorLogger   internal.LogWriter = streamLoggerInstance
)

func SetLogLevel(level LogLevel) {
	debugLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Debug {
		debugLogger = streamLoggerInstance
	}

	infoLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Info {
		infoLogger = streamLoggerInstance
	}

	warningLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Warning {
		warningLogger = streamLoggerInstance
	}

	errorLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Error {
		errorLogger = streamLoggerInstance
	}
}

func InitErrorLogger(file string) error {
	logger, err := internal.NewFileLogWriter(file)
	if err != nil {
		return newError("failed to create error logger on file (", file, ")").Base(err)
	}
	streamLoggerInstance = logger
	return nil
}

func getLoggerAndPrefix(s errors.Severity) (internal.LogWriter, string) {
	switch s {
	case errors.SeverityDebug:
		return debugLogger, "[Debug]"
	case errors.SeverityInfo:
		return infoLogger, "[Info]"
	case errors.SeverityWarning:
		return warningLogger, "[Warning]"
	case errors.SeverityError:
		return errorLogger, "[Error]"
	default:
		return infoLogger, "[Info]"
	}
}

// Trace logs an error message based on its severity.
func Trace(err error) {
	logger, prefix := getLoggerAndPrefix(errors.GetSeverity(err))
	logger.Log(&internal.ErrorLog{
		Prefix: prefix,
		Error:  err,
	})
}

type Instance struct {
	config *Config
}

func New(ctx context.Context, config *Config) (*Instance, error) {
	return &Instance{config: config}, nil
}

func (*Instance) Interface() interface{} {
	return (*Instance)(nil)
}

func (g *Instance) Start() error {
	config := g.config
	if config.AccessLogType == LogType_File {
		if err := InitAccessLogger(config.AccessLogPath); err != nil {
			return err
		}
	}

	if config.ErrorLogType == LogType_None {
		SetLogLevel(LogLevel_Disabled)
	} else {
		if config.ErrorLogType == LogType_File {
			if err := InitErrorLogger(config.ErrorLogPath); err != nil {
				return err
			}
		}
		SetLogLevel(config.ErrorLogLevel)
	}

	return nil
}

func (*Instance) Close() {
	streamLoggerInstance.Close()
	accessLoggerInstance.Close()
}

func FromSpace(space app.Space) *Instance {
	v := space.GetApplication((*Instance)(nil))
	if logger, ok := v.(*Instance); ok && logger != nil {
		return logger
	}
	return nil
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return New(ctx, config.(*Config))
	}))
}
<commit_msg>trace only non nil error<commit_after>package log

//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg log -path App,Log

import (
	"context"

	"v2ray.com/core/app"
	"v2ray.com/core/app/log/internal"
	"v2ray.com/core/common"
	"v2ray.com/core/common/errors"
)

var (
	streamLoggerInstance internal.LogWriter = internal.NewStdOutLogWriter()

	debugLogger   internal.LogWriter = streamLoggerInstance
	infoLogger    internal.LogWriter = streamLoggerInstance
	warningLogger internal.LogWriter = streamLoggerInstance
	errorLogger   internal.LogWriter = streamLoggerInstance
)

func SetLogLevel(level LogLevel) {
	debugLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Debug {
		debugLogger = streamLoggerInstance
	}

	infoLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Info {
		infoLogger = streamLoggerInstance
	}

	warningLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Warning {
		warningLogger = streamLoggerInstance
	}

	errorLogger = new(internal.NoOpLogWriter)
	if level >= LogLevel_Error {
		errorLogger = streamLoggerInstance
	}
}

func InitErrorLogger(file string) error {
	logger, err := internal.NewFileLogWriter(file)
	if err != nil {
		return newError("failed to create error logger on file (", file, ")").Base(err)
	}
	streamLoggerInstance = logger
	return nil
}

func getLoggerAndPrefix(s errors.Severity) (internal.LogWriter, string) {
	switch s {
	case errors.SeverityDebug:
		return debugLogger, "[Debug]"
	case errors.SeverityInfo:
		return infoLogger, "[Info]"
	case errors.SeverityWarning:
		return warningLogger, "[Warning]"
	case errors.SeverityError:
		return errorLogger, "[Error]"
	default:
		return infoLogger, "[Info]"
	}
}

// Trace logs an error message based on its severity.
func Trace(err error) {
	if err == nil {
		return
	}
	logger, prefix := getLoggerAndPrefix(errors.GetSeverity(err))
	logger.Log(&internal.ErrorLog{
		Prefix: prefix,
		Error:  err,
	})
}

type Instance struct {
	config *Config
}

func New(ctx context.Context, config *Config) (*Instance, error) {
	return &Instance{config: config}, nil
}

func (*Instance) Interface() interface{} {
	return (*Instance)(nil)
}

func (g *Instance) Start() error {
	config := g.config
	if config.AccessLogType == LogType_File {
		if err := InitAccessLogger(config.AccessLogPath); err != nil {
			return err
		}
	}

	if config.ErrorLogType == LogType_None {
		SetLogLevel(LogLevel_Disabled)
	} else {
		if config.ErrorLogType == LogType_File {
			if err := InitErrorLogger(config.ErrorLogPath); err != nil {
				return err
			}
		}
		SetLogLevel(config.ErrorLogLevel)
	}

	return nil
}

func (*Instance) Close() {
	streamLoggerInstance.Close()
	accessLoggerInstance.Close()
}

func FromSpace(space app.Space) *Instance {
	v := space.GetApplication((*Instance)(nil))
	if logger, ok := v.(*Instance); ok && logger != nil {
		return logger
	}
	return nil
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return New(ctx, config.(*Config))
	}))
}
<|endoftext|>


<commit_before>package aws

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

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/mitchellh/goamz/route53"
)

func resourceAwsRoute53Record() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRoute53RecordCreate,
		Read:   resourceAwsRoute53RecordRead,
		Delete: resourceAwsRoute53RecordDelete,

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

			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"ttl": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"records": &schema.Schema{
				Type:     schema.TypeList,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Required: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53

	// Get the record
	rec, err := resourceAwsRoute53RecordBuildSet(d)
	if err != nil {
		return err
	}

	// Create the new records. We abuse StateChangeConf for this to
	// retry for us since Route53 sometimes returns errors about another
	// operation happening at the same time.
	req := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Managed by Terraform",
		Changes: []route53.Change{
			route53.Change{
				Action: "UPSERT",
				Record: *rec,
			},
		},
	}
	zone := d.Get("zone_id").(string)
	log.Printf("[DEBUG] Creating resource records for zone: %s, name: %s",
		zone, d.Get("name").(string))

	wait := resource.StateChangeConf{
		Pending:    []string{"rejected"},
		Target:     "accepted",
		Timeout:    5 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			resp, err := conn.ChangeResourceRecordSets(zone, req)
			if err != nil {
				if strings.Contains(err.Error(), "PriorRequestNotComplete") {
					// There is some pending operation, so just retry
					// in a bit.
					return nil, "rejected", nil
				}

				return nil, "failure", err
			}

			return resp.ChangeInfo, "accepted", nil
		},
	}

	respRaw, err := wait.WaitForState()
	if err != nil {
		return err
	}
	changeInfo := respRaw.(route53.ChangeInfo)

	// Generate an ID
	d.SetId(fmt.Sprintf("%s_%s_%s", zone, d.Get("name").(string), d.Get("type").(string)))

	// Wait until we are done
	wait = resource.StateChangeConf{
		Delay:      30 * time.Second,
		Pending:    []string{"PENDING"},
		Target:     "INSYNC",
		Timeout:    10 * time.Minute,
		MinTimeout: 5 * time.Second,
		Refresh: func() (result interface{}, state string, err error) {
			return resourceAwsRoute53Wait(conn, changeInfo.ID)
		},
	}
	_, err = wait.WaitForState()
	if err != nil {
		return err
	}

	return nil
}

func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53

	zone := d.Get("zone_id").(string)
	lopts := &route53.ListOpts{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
	}
	resp, err := conn.ListResourceRecordSets(zone, lopts)
	if err != nil {
		return err
	}

	// Scan for a matching record
	found := false
	for _, record := range resp.Records {
		if route53.FQDN(record.Name) != route53.FQDN(lopts.Name) {
			continue
		}
		if strings.ToUpper(record.Type) != strings.ToUpper(lopts.Type) {
			continue
		}

		found = true

		for i, rec := range record.Records {
			key := fmt.Sprintf("records.%d", i)
			d.Set(key, rec)
		}
		d.Set("ttl", record.TTL)

		break
	}

	if !found {
		d.SetId("")
	}

	return nil
}

func resourceAwsRoute53RecordDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53

	// Get the records
	rec, err := resourceAwsRoute53RecordBuildSet(d)
	if err != nil {
		return err
	}

	// Create the new records
	req := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Deleted by Terraform",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: *rec,
			},
		},
	}
	zone := d.Get("zone_id").(string)
	log.Printf("[DEBUG] Deleting resource records for zone: %s, name: %s",
		zone, d.Get("name").(string))

	wait := resource.StateChangeConf{
		Pending:    []string{"rejected"},
		Target:     "accepted",
		Timeout:    5 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			_, err := conn.ChangeResourceRecordSets(zone, req)
			if err != nil {
				if strings.Contains(err.Error(), "PriorRequestNotComplete") {
					// There is some pending operation, so just retry
					// in a bit.
					return 42, "rejected", nil
				}

				if strings.Contains(err.Error(), "InvalidChangeBatch") {
					// This means that the record is already gone.
					return 42, "accepted", nil
				}

				return 42, "failure", err
			}

			return 42, "accepted", nil
		},
	}

	if _, err := wait.WaitForState(); err != nil {
		return err
	}

	return nil
}

func resourceAwsRoute53RecordBuildSet(d *schema.ResourceData) (*route53.ResourceRecordSet, error) {
	recs := d.Get("records.#").(int)
	records := make([]string, 0, recs)
	for i := 0; i < recs; i++ {
		key := fmt.Sprintf("records.%d", i)
		records = append(records, d.Get(key).(string))
	}

	rec := &route53.ResourceRecordSet{
		Name:    d.Get("name").(string),
		Type:    d.Get("type").(string),
		TTL:     d.Get("ttl").(int),
		Records: records,
	}
	return rec, nil
}
<commit_msg>Fixing the flipflop problem<commit_after>package aws

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

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/mitchellh/goamz/route53"
)

func resourceAwsRoute53Record() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRoute53RecordCreate,
		Read:   resourceAwsRoute53RecordRead,
		Delete: resourceAwsRoute53RecordDelete,

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

			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"ttl": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"records": &schema.Schema{
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Required: true,
				ForceNew: true,
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
		},
	}
}

func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53

	// Get the record
	rec, err := resourceAwsRoute53RecordBuildSet(d)
	if err != nil {
		return err
	}

	// Create the new records. We abuse StateChangeConf for this to
	// retry for us since Route53 sometimes returns errors about another
	// operation happening at the same time.
	req := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Managed by Terraform",
		Changes: []route53.Change{
			route53.Change{
				Action: "UPSERT",
				Record: *rec,
			},
		},
	}
	zone := d.Get("zone_id").(string)
	log.Printf("[DEBUG] Creating resource records for zone: %s, name: %s",
		zone, d.Get("name").(string))

	wait := resource.StateChangeConf{
		Pending:    []string{"rejected"},
		Target:     "accepted",
		Timeout:    5 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			resp, err := conn.ChangeResourceRecordSets(zone, req)
			if err != nil {
				if strings.Contains(err.Error(), "PriorRequestNotComplete") {
					// There is some pending operation, so just retry
					// in a bit.
					return nil, "rejected", nil
				}

				return nil, "failure", err
			}

			return resp.ChangeInfo, "accepted", nil
		},
	}

	respRaw, err := wait.WaitForState()
	if err != nil {
		return err
	}
	changeInfo := respRaw.(route53.ChangeInfo)

	// Generate an ID
	d.SetId(fmt.Sprintf("%s_%s_%s", zone, d.Get("name").(string), d.Get("type").(string)))

	// Wait until we are done
	wait = resource.StateChangeConf{
		Delay:      30 * time.Second,
		Pending:    []string{"PENDING"},
		Target:     "INSYNC",
		Timeout:    10 * time.Minute,
		MinTimeout: 5 * time.Second,
		Refresh: func() (result interface{}, state string, err error) {
			return resourceAwsRoute53Wait(conn, changeInfo.ID)
		},
	}
	_, err = wait.WaitForState()
	if err != nil {
		return err
	}

	return nil
}

func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53

	zone := d.Get("zone_id").(string)
	lopts := &route53.ListOpts{
		Name: d.Get("name").(string),
		Type: d.Get("type").(string),
	}
	resp, err := conn.ListResourceRecordSets(zone, lopts)
	if err != nil {
		return err
	}

	// Scan for a matching record
	found := false
	for _, record := range resp.Records {
		if route53.FQDN(record.Name) != route53.FQDN(lopts.Name) {
			continue
		}
		if strings.ToUpper(record.Type) != strings.ToUpper(lopts.Type) {
			continue
		}

		found = true

		// Create an empty schema.Set to hold all found records
		records := &schema.Set{
			F: func(v interface{}) int {
				return hashcode.String(v.(string))
			},
		}

		for _, rec := range record.Records {
			records.Add(rec)
		}

		d.Set("records", records)
		d.Set("ttl", record.TTL)

		break
	}

	if !found {
		d.SetId("")
	}

	return nil
}

func resourceAwsRoute53RecordDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53

	// Get the records
	rec, err := resourceAwsRoute53RecordBuildSet(d)
	if err != nil {
		return err
	}

	// Create the new records
	req := &route53.ChangeResourceRecordSetsRequest{
		Comment: "Deleted by Terraform",
		Changes: []route53.Change{
			route53.Change{
				Action: "DELETE",
				Record: *rec,
			},
		},
	}
	zone := d.Get("zone_id").(string)
	log.Printf("[DEBUG] Deleting resource records for zone: %s, name: %s",
		zone, d.Get("name").(string))

	wait := resource.StateChangeConf{
		Pending:    []string{"rejected"},
		Target:     "accepted",
		Timeout:    5 * time.Minute,
		MinTimeout: 1 * time.Second,
		Refresh: func() (interface{}, string, error) {
			_, err := conn.ChangeResourceRecordSets(zone, req)
			if err != nil {
				if strings.Contains(err.Error(), "PriorRequestNotComplete") {
					// There is some pending operation, so just retry
					// in a bit.
					return 42, "rejected", nil
				}

				if strings.Contains(err.Error(), "InvalidChangeBatch") {
					// This means that the record is already gone.
					return 42, "accepted", nil
				}

				return 42, "failure", err
			}

			return 42, "accepted", nil
		},
	}

	if _, err := wait.WaitForState(); err != nil {
		return err
	}

	return nil
}

func resourceAwsRoute53RecordBuildSet(d *schema.ResourceData) (*route53.ResourceRecordSet, error) {
	recs := d.Get("records.#").(int)
	records := make([]string, 0, recs)
	for i := 0; i < recs; i++ {
		key := fmt.Sprintf("records.%d", i)
		records = append(records, d.Get(key).(string))
	}

	rec := &route53.ResourceRecordSet{
		Name:    d.Get("name").(string),
		Type:    d.Get("type").(string),
		TTL:     d.Get("ttl").(int),
		Records: records,
	}
	return rec, nil
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/af83/edwig/audit"
	"github.com/af83/edwig/logger"
	"github.com/af83/edwig/model"
	"github.com/af83/edwig/siri"
)

type SIRIStopMonitoringBroadcaster interface {
	model.Stopable
	model.Startable
}

type SMBroadcaster struct {
	model.ClockConsumer

	connector *SIRIStopMonitoringSubscriptionBroadcaster
}

type StopMonitoringBroadcaster struct {
	SMBroadcaster

	stop chan struct{}
}

type FakeStopMonitoringBroadcaster struct {
	SMBroadcaster

	model.ClockConsumer
}

func NewFakeStopMonitoringBroadcaster(connector *SIRIStopMonitoringSubscriptionBroadcaster) SIRIStopMonitoringBroadcaster {
	broadcaster := &FakeStopMonitoringBroadcaster{}
	broadcaster.connector = connector
	return broadcaster
}

func (broadcaster *FakeStopMonitoringBroadcaster) Start() {
	broadcaster.prepareSIRIStopMonitoringNotify()
}

func (broadcaster *FakeStopMonitoringBroadcaster) Stop() {}

func NewSIRIStopMonitoringBroadcaster(connector *SIRIStopMonitoringSubscriptionBroadcaster) SIRIStopMonitoringBroadcaster {
	broadcaster := &StopMonitoringBroadcaster{}
	broadcaster.connector = connector

	return broadcaster
}

func (smb *StopMonitoringBroadcaster) Start() {
	logger.Log.Debugf("Start StopMonitoringBroadcaster")

	smb.stop = make(chan struct{})
	go smb.run()
}

func (smb *StopMonitoringBroadcaster) run() {
	c := smb.Clock().After(5 * time.Second)

	for {
		select {
		case <-smb.stop:
			logger.Log.Debugf("stop monitoring broadcaster routine stop")

			return
		case <-c:
			logger.Log.Debugf("SIRIStopMonitoringBroadcaster visit")

			smb.prepareSIRIStopMonitoringNotify()

			c = smb.Clock().After(5 * time.Second)
		}
	}
}

func (smb *StopMonitoringBroadcaster) Stop() {
	if smb.stop != nil {
		close(smb.stop)
	}
}

func (smb *SMBroadcaster) prepareSIRIStopMonitoringNotify() {
	smb.connector.mutex.Lock()

	events := smb.connector.toBroadcast
	smb.connector.toBroadcast = make(map[SubscriptionId][]model.StopVisitId)

	smb.connector.mutex.Unlock()

	tx := smb.connector.Partner().Referential().NewTransaction()
	defer tx.Close()

	for key, stopVisits := range events {
		sub, ok := smb.connector.Partner().Subscriptions().Find(key)
		if !ok {
			continue
		}

		// Initialize builder
		stopMonitoringBuilder := NewBroadcastStopMonitoringBuilder(tx, smb.connector.SIRIPartner(), SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER)
		stopMonitoringBuilder.StopVisitTypes = sub.SubscriptionOptions()["StopVisitTypes"]

		maximumStopVisits, _ := strconv.Atoi(sub.SubscriptionOptions()["MaximumStopVisits"])
		monitoredStopVisits := make(map[model.StopVisitId]struct{}) //Making sure not to send 2 times the same SV

		delivery := &siri.SIRINotifyStopMonitoring{
			Address:                   smb.connector.Partner().Address(),
			ProducerRef:               smb.connector.Partner().ProducerRef(),
			ResponseMessageIdentifier: smb.connector.SIRIPartner().IdentifierGenerator("response_message_identifier").NewMessageIdentifier(),
			SubscriberRef:             smb.connector.SIRIPartner().RequestorRef(),
			SubscriptionIdentifier:    sub.ExternalId(),
			ResponseTimestamp:         smb.connector.Clock().Now(),
			Status:                    true,
			RequestMessageRef:         sub.SubscriptionOptions()["MessageIdentifier"],
		}

		for _, stopVisitId := range stopVisits {
			// Check if resource is already in the map
			if _, ok := monitoredStopVisits[stopVisitId]; ok {
				continue
			}

			// Find the StopVisit
			stopVisit, ok := tx.Model().StopVisits().Find(stopVisitId)
			if !ok {
				continue
			}

			// Find the Resource ObjectId
			stopArea, ok := tx.Model().StopAreas().Find(stopVisit.StopAreaId)
			if !ok {
				continue
			}
			objectid, ok := stopArea.ObjectID(smb.connector.Partner().RemoteObjectIDKind(SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER))
			if !ok {
				continue
			}

			// Find the Resource
			resource := sub.Resource(objectid)
			if resource == nil {
				continue
			}

			// Get the monitoredStopVisit
			stopMonitoringBuilder.MonitoringRef = objectid.Value()
			if !smb.handledStopVisitAppend(stopVisit, delivery, stopMonitoringBuilder) {
				continue
			}

			monitoredStopVisits[stopVisitId] = struct{}{}

			// Refresh delivery
			if maximumStopVisits != 0 && (len(delivery.MonitoredStopVisits)+len(delivery.CancelledStopVisits)) >= maximumStopVisits {
				smb.sendDelivery(delivery)
				delivery.MonitoredStopVisits = []*siri.SIRIMonitoredStopVisit{}
				delivery.CancelledStopVisits = []*siri.SIRICancelledStopVisit{}
			}

			// Get the Resource lastState for the StopVisit
			lastStateInterface, _ := resource.LastStates[string(stopVisitId)]
			lastState, ok := lastStateInterface.(*stopMonitoringLastChange)
			if !ok {
				continue
			}
			lastState.UpdateState(&stopVisit)
		}
		if len(delivery.MonitoredStopVisits) != 0 || len(delivery.CancelledStopVisits) != 0 {
			smb.sendDelivery(delivery)
		}
	}
}

func (smb *SMBroadcaster) handledStopVisitAppend(stopVisit model.StopVisit, delivery *siri.SIRINotifyStopMonitoring, stopMonitoringBuilder *BroadcastStopMonitoringBuilder) bool {

	if stopVisit.ArrivalStatus == model.STOP_VISIT_ARRIVAL_CANCELLED || stopVisit.DepartureStatus == model.STOP_VISIT_DEPARTURE_CANCELLED {
		return smb.handleCancelledStopVisit(stopVisit, delivery, stopMonitoringBuilder)
	} else {
		return smb.handleMonitoredStopVisit(stopVisit, delivery, stopMonitoringBuilder)
	}
}

func (smb *SMBroadcaster) handleCancelledStopVisit(stopVisit model.StopVisit, delivery *siri.SIRINotifyStopMonitoring, stopMonitoringBuilder *BroadcastStopMonitoringBuilder) bool {
	cancelledStopVisit := stopMonitoringBuilder.BuildCancelledStopVisit(stopVisit)
	if cancelledStopVisit == nil {
		return false
	}

	delivery.CancelledStopVisits = append(delivery.CancelledStopVisits, cancelledStopVisit)
	return true
}

func (smb *SMBroadcaster) handleMonitoredStopVisit(stopVisit model.StopVisit, delivery *siri.SIRINotifyStopMonitoring, stopMonitoringBuilder *BroadcastStopMonitoringBuilder) bool {
	monitoredStopVisit := stopMonitoringBuilder.BuildMonitoredStopVisit(stopVisit)
	if monitoredStopVisit == nil {
		return false
	}
	delivery.MonitoredStopVisits = append(delivery.MonitoredStopVisits, monitoredStopVisit)
	return true
}

func (smb *SMBroadcaster) sendDelivery(delivery *siri.SIRINotifyStopMonitoring) {
	logStashEvent := smb.newLogStashEvent()
	logSIRIStopMonitoringNotify(logStashEvent, delivery)
	audit.CurrentLogStash().WriteEvent(logStashEvent)

	err := smb.connector.SIRIPartner().SOAPClient().NotifyStopMonitoring(delivery)
	if err != nil {
		event := smb.newLogStashEvent()
		logSIRINotifyError(err.Error(), event)
		audit.CurrentLogStash().WriteEvent(event)
	}
}

func (smb *SMBroadcaster) newLogStashEvent() audit.LogStashEvent {
	event := smb.connector.partner.NewLogStashEvent()
	event["connector"] = "StopMonitoringSubscriptionBroadcaster"
	return event
}

func logSIRIStopMonitoringNotify(logStashEvent audit.LogStashEvent, response *siri.SIRINotifyStopMonitoring) {
	monitoringRefs := []string{}

	for _, sv := range response.MonitoredStopVisits {
		monitoringRefs = append(monitoringRefs, sv.MonitoringRef)
	}

	logStashEvent["type"] = "NotifyStopMonitoring"
	logStashEvent["producerRef"] = response.ProducerRef
	logStashEvent["requestMessageRef"] = response.RequestMessageRef
	logStashEvent["responseMessageIdentifier"] = response.ResponseMessageIdentifier
	logStashEvent["responseTimestamp"] = response.ResponseTimestamp.String()
	logStashEvent["subscriberRef"] = response.SubscriberRef
	logStashEvent["subscriptionIdentifier"] = response.SubscriptionIdentifier
	logStashEvent["monitoringRef"] = strings.Join(monitoringRefs, ",")
	logStashEvent["status"] = strconv.FormatBool(response.Status)
	if !response.Status {
		logStashEvent["errorType"] = response.ErrorType
		logStashEvent["errorNumber"] = strconv.Itoa(response.ErrorNumber)
		logStashEvent["errorText"] = response.ErrorText
	}
	xml, err := response.BuildXML()
	if err != nil {
		logStashEvent["responseXML"] = fmt.Sprintf("%v", err)
		return
	}
	logStashEvent["responseXML"] = xml
}
<commit_msg>add CancelledStopVisits to logs in SMBroadcaster<commit_after>package core

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/af83/edwig/audit"
	"github.com/af83/edwig/logger"
	"github.com/af83/edwig/model"
	"github.com/af83/edwig/siri"
)

type SIRIStopMonitoringBroadcaster interface {
	model.Stopable
	model.Startable
}

type SMBroadcaster struct {
	model.ClockConsumer

	connector *SIRIStopMonitoringSubscriptionBroadcaster
}

type StopMonitoringBroadcaster struct {
	SMBroadcaster

	stop chan struct{}
}

type FakeStopMonitoringBroadcaster struct {
	SMBroadcaster

	model.ClockConsumer
}

func NewFakeStopMonitoringBroadcaster(connector *SIRIStopMonitoringSubscriptionBroadcaster) SIRIStopMonitoringBroadcaster {
	broadcaster := &FakeStopMonitoringBroadcaster{}
	broadcaster.connector = connector
	return broadcaster
}

func (broadcaster *FakeStopMonitoringBroadcaster) Start() {
	broadcaster.prepareSIRIStopMonitoringNotify()
}

func (broadcaster *FakeStopMonitoringBroadcaster) Stop() {}

func NewSIRIStopMonitoringBroadcaster(connector *SIRIStopMonitoringSubscriptionBroadcaster) SIRIStopMonitoringBroadcaster {
	broadcaster := &StopMonitoringBroadcaster{}
	broadcaster.connector = connector

	return broadcaster
}

func (smb *StopMonitoringBroadcaster) Start() {
	logger.Log.Debugf("Start StopMonitoringBroadcaster")

	smb.stop = make(chan struct{})
	go smb.run()
}

func (smb *StopMonitoringBroadcaster) run() {
	c := smb.Clock().After(5 * time.Second)

	for {
		select {
		case <-smb.stop:
			logger.Log.Debugf("stop monitoring broadcaster routine stop")

			return
		case <-c:
			logger.Log.Debugf("SIRIStopMonitoringBroadcaster visit")

			smb.prepareSIRIStopMonitoringNotify()

			c = smb.Clock().After(5 * time.Second)
		}
	}
}

func (smb *StopMonitoringBroadcaster) Stop() {
	if smb.stop != nil {
		close(smb.stop)
	}
}

func (smb *SMBroadcaster) prepareSIRIStopMonitoringNotify() {
	smb.connector.mutex.Lock()

	events := smb.connector.toBroadcast
	smb.connector.toBroadcast = make(map[SubscriptionId][]model.StopVisitId)

	smb.connector.mutex.Unlock()

	tx := smb.connector.Partner().Referential().NewTransaction()
	defer tx.Close()

	for key, stopVisits := range events {
		sub, ok := smb.connector.Partner().Subscriptions().Find(key)
		if !ok {
			continue
		}

		// Initialize builder
		stopMonitoringBuilder := NewBroadcastStopMonitoringBuilder(tx, smb.connector.SIRIPartner(), SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER)
		stopMonitoringBuilder.StopVisitTypes = sub.SubscriptionOptions()["StopVisitTypes"]

		maximumStopVisits, _ := strconv.Atoi(sub.SubscriptionOptions()["MaximumStopVisits"])
		monitoredStopVisits := make(map[model.StopVisitId]struct{}) //Making sure not to send 2 times the same SV

		delivery := &siri.SIRINotifyStopMonitoring{
			Address:                   smb.connector.Partner().Address(),
			ProducerRef:               smb.connector.Partner().ProducerRef(),
			ResponseMessageIdentifier: smb.connector.SIRIPartner().IdentifierGenerator("response_message_identifier").NewMessageIdentifier(),
			SubscriberRef:             smb.connector.SIRIPartner().RequestorRef(),
			SubscriptionIdentifier:    sub.ExternalId(),
			ResponseTimestamp:         smb.connector.Clock().Now(),
			Status:                    true,
			RequestMessageRef:         sub.SubscriptionOptions()["MessageIdentifier"],
		}

		for _, stopVisitId := range stopVisits {
			// Check if resource is already in the map
			if _, ok := monitoredStopVisits[stopVisitId]; ok {
				continue
			}

			// Find the StopVisit
			stopVisit, ok := tx.Model().StopVisits().Find(stopVisitId)
			if !ok {
				continue
			}

			// Find the Resource ObjectId
			stopArea, ok := tx.Model().StopAreas().Find(stopVisit.StopAreaId)
			if !ok {
				continue
			}
			objectid, ok := stopArea.ObjectID(smb.connector.Partner().RemoteObjectIDKind(SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER))
			if !ok {
				continue
			}

			// Find the Resource
			resource := sub.Resource(objectid)
			if resource == nil {
				continue
			}

			// Get the monitoredStopVisit
			stopMonitoringBuilder.MonitoringRef = objectid.Value()
			if !smb.handledStopVisitAppend(stopVisit, delivery, stopMonitoringBuilder) {
				continue
			}

			monitoredStopVisits[stopVisitId] = struct{}{}

			// Refresh delivery
			if maximumStopVisits != 0 && (len(delivery.MonitoredStopVisits)+len(delivery.CancelledStopVisits)) >= maximumStopVisits {
				smb.sendDelivery(delivery)
				delivery.MonitoredStopVisits = []*siri.SIRIMonitoredStopVisit{}
				delivery.CancelledStopVisits = []*siri.SIRICancelledStopVisit{}
			}

			// Get the Resource lastState for the StopVisit
			lastStateInterface, _ := resource.LastStates[string(stopVisitId)]
			lastState, ok := lastStateInterface.(*stopMonitoringLastChange)
			if !ok {
				continue
			}
			lastState.UpdateState(&stopVisit)
		}
		if len(delivery.MonitoredStopVisits) != 0 || len(delivery.CancelledStopVisits) != 0 {
			smb.sendDelivery(delivery)
		}
	}
}

func (smb *SMBroadcaster) handledStopVisitAppend(stopVisit model.StopVisit, delivery *siri.SIRINotifyStopMonitoring, stopMonitoringBuilder *BroadcastStopMonitoringBuilder) bool {

	if stopVisit.ArrivalStatus == model.STOP_VISIT_ARRIVAL_CANCELLED || stopVisit.DepartureStatus == model.STOP_VISIT_DEPARTURE_CANCELLED {
		return smb.handleCancelledStopVisit(stopVisit, delivery, stopMonitoringBuilder)
	} else {
		return smb.handleMonitoredStopVisit(stopVisit, delivery, stopMonitoringBuilder)
	}
}

func (smb *SMBroadcaster) handleCancelledStopVisit(stopVisit model.StopVisit, delivery *siri.SIRINotifyStopMonitoring, stopMonitoringBuilder *BroadcastStopMonitoringBuilder) bool {
	cancelledStopVisit := stopMonitoringBuilder.BuildCancelledStopVisit(stopVisit)
	if cancelledStopVisit == nil {
		return false
	}

	delivery.CancelledStopVisits = append(delivery.CancelledStopVisits, cancelledStopVisit)
	return true
}

func (smb *SMBroadcaster) handleMonitoredStopVisit(stopVisit model.StopVisit, delivery *siri.SIRINotifyStopMonitoring, stopMonitoringBuilder *BroadcastStopMonitoringBuilder) bool {
	monitoredStopVisit := stopMonitoringBuilder.BuildMonitoredStopVisit(stopVisit)
	if monitoredStopVisit == nil {
		return false
	}
	delivery.MonitoredStopVisits = append(delivery.MonitoredStopVisits, monitoredStopVisit)
	return true
}

func (smb *SMBroadcaster) sendDelivery(delivery *siri.SIRINotifyStopMonitoring) {
	logStashEvent := smb.newLogStashEvent()
	logSIRIStopMonitoringNotify(logStashEvent, delivery)
	audit.CurrentLogStash().WriteEvent(logStashEvent)

	err := smb.connector.SIRIPartner().SOAPClient().NotifyStopMonitoring(delivery)
	if err != nil {
		event := smb.newLogStashEvent()
		logSIRINotifyError(err.Error(), event)
		audit.CurrentLogStash().WriteEvent(event)
	}
}

func (smb *SMBroadcaster) newLogStashEvent() audit.LogStashEvent {
	event := smb.connector.partner.NewLogStashEvent()
	event["connector"] = "StopMonitoringSubscriptionBroadcaster"
	return event
}

func logSIRIStopMonitoringNotify(logStashEvent audit.LogStashEvent, response *siri.SIRINotifyStopMonitoring) {
	monitoringRefs := []string{}
	for _, sv := range response.MonitoredStopVisits {
		monitoringRefs = append(monitoringRefs, sv.MonitoringRef)
	}

	cancelledMonitoringRefs := []string{}
	for _, sv := range response.CancelledStopVisits {
		cancelledMonitoringRefs = append(cancelledMonitoringRefs, sv.MonitoringRef)
	}

	logStashEvent["type"] = "NotifyStopMonitoring"
	logStashEvent["producerRef"] = response.ProducerRef
	logStashEvent["requestMessageRef"] = response.RequestMessageRef
	logStashEvent["responseMessageIdentifier"] = response.ResponseMessageIdentifier
	logStashEvent["responseTimestamp"] = response.ResponseTimestamp.String()
	logStashEvent["subscriberRef"] = response.SubscriberRef
	logStashEvent["subscriptionIdentifier"] = response.SubscriptionIdentifier
	logStashEvent["monitoringRef"] = strings.Join(monitoringRefs, ",")
	logStashEvent["cancelledMonitoringRef"] = strings.Join(cancelledMonitoringRefs, ",")
	logStashEvent["status"] = strconv.FormatBool(response.Status)
	if !response.Status {
		logStashEvent["errorType"] = response.ErrorType
		logStashEvent["errorNumber"] = strconv.Itoa(response.ErrorNumber)
		logStashEvent["errorText"] = response.ErrorText
	}
	xml, err := response.BuildXML()
	if err != nil {
		logStashEvent["responseXML"] = fmt.Sprintf("%v", err)
		return
	}
	logStashEvent["responseXML"] = xml
}
<|endoftext|>


<commit_before>// Package websocketproxy is a reverse proxy for WebSocket connections.
package websocketproxy

import (
	"io"
	"log"
	"net/http"
	"net/url"

	"github.com/gorilla/websocket"
)

var (
	// DefaultUpgrader specifies the paramaters for upgrading an HTTP
	// connection to a WebSocket connection.
	DefaultUpgrader = &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}

	// DefaultDialer is a dialer with all fields set to the default zero values.
	DefaultDialer = websocket.DefaultDialer
)

// WebsocketProxy is an HTTP Handler that takes an incoming websocket
// connection and proxies it to another server.
type WebsocketProxy struct {
	// Backend returns the backend URL which the proxy uses to reverse proxy
	// the incoming websocket connection. Request is the initial incoming and
	// unmodified request.
	Backend func(*http.Request) *url.URL

	// Upgrader specifies the paramaters for upgrading an HTTP connection to a
	// WebSocket connection. If nil, DefaultUpgrader is used.
	Upgrader *websocket.Upgrader

	//  Dialer contains options for connecting to WebSocket server. If nil,
	//  DefaultDialer is used.
	Dialer *websocket.Dialer
}

// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		NewProxy(target).ServeHTTP(rw, req)
	})
}

// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
	backend := func(r *http.Request) *url.URL { return target }
	return &WebsocketProxy{Backend: backend}
}

// ServeHTTP implements the http.Handler that proxies WebSocket connections.
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	upgrader := w.Upgrader
	if w.Upgrader == nil {
		upgrader = DefaultUpgrader
	}

	connPub, err := upgrader.Upgrade(rw, req, nil)
	if err != nil {
		log.Printf("websocketproxy: couldn't upgrade %s\n", err)
		return
	}
	defer connPub.Close()

	backendURL := w.Backend(req)

	dialer := w.Dialer
	if w.Dialer == nil {
		dialer = DefaultDialer
	}

	connBackend, _, err := dialer.Dial(backendURL.String(), nil)
	if err != nil {
		log.Printf("websocketproxy: couldn't dial to remote backend url %s\n", err)
		return
	}
	defer connBackend.Close()

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}

	go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
	go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
	<-errc
}
<commit_msg>websocket: copy headers from incoming request to client<commit_after>// Package websocketproxy is a reverse proxy for WebSocket connections.
package websocketproxy

import (
	"io"
	"log"
	"net/http"
	"net/url"

	"github.com/gorilla/websocket"
)

var (
	// DefaultUpgrader specifies the paramaters for upgrading an HTTP
	// connection to a WebSocket connection.
	DefaultUpgrader = &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}

	// DefaultDialer is a dialer with all fields set to the default zero values.
	DefaultDialer = websocket.DefaultDialer
)

// WebsocketProxy is an HTTP Handler that takes an incoming websocket
// connection and proxies it to another server.
type WebsocketProxy struct {
	// Backend returns the backend URL which the proxy uses to reverse proxy
	// the incoming websocket connection. Request is the initial incoming and
	// unmodified request.
	Backend func(*http.Request) *url.URL

	// Upgrader specifies the paramaters for upgrading an HTTP connection to a
	// WebSocket connection. If nil, DefaultUpgrader is used.
	Upgrader *websocket.Upgrader

	//  Dialer contains options for connecting to WebSocket server. If nil,
	//  DefaultDialer is used.
	Dialer *websocket.Dialer
}

// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		NewProxy(target).ServeHTTP(rw, req)
	})
}

// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
	backend := func(r *http.Request) *url.URL { return target }
	return &WebsocketProxy{Backend: backend}
}

// ServeHTTP implements the http.Handler that proxies WebSocket connections.
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	backendURL := w.Backend(req)

	dialer := w.Dialer
	if w.Dialer == nil {
		dialer = DefaultDialer
	}

	// Pass headers from the incoming request to the dialer to forward them to
	// the final destinations.
	h := http.Header{}
	h.Add("Origin", req.Header.Get("Origin"))
	protocols := req.Header["Sec-WebSocket-Protocol"]
	for _, prot := range protocols {
		h.Add("Sec-WebSocket-Protocol", prot)
	}
	cookies := req.Header["Cookie"]
	for _, cookie := range cookies {
		h.Add("Cookie", cookie)
	}

	// Connect to the backend url, also pass the headers we prepared above.
	connBackend, resp, err := dialer.Dial(backendURL.String(), h)
	if err != nil {
		log.Printf("websocketproxy: couldn't dial to remote backend url %s\n", err)
		return
	}
	defer connBackend.Close()

	upgrader := w.Upgrader
	if w.Upgrader == nil {
		upgrader = DefaultUpgrader
	}

	// Now upgrade the existing incoming request to a WebSocket connection.
	// Also pass the responseHeader that we gathered from the Dial handshake.
	connPub, err := upgrader.Upgrade(rw, req, resp.Header)
	if err != nil {
		log.Printf("websocketproxy: couldn't upgrade %s\n", err)
		return
	}
	defer connPub.Close()

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}

	// Start our proxy now, after we setup everything.
	go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
	go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
	<-errc
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/justincampbell/zipcode"
	"github.com/mlbright/forecast/v2"
)

var conditionIcons = map[string]string{
	"clear-day":           "☀",
	"clear-night":         "🌙",
	"cloudy":              "☁",
	"fog":                 "🌁",
	"partly-cloudy-day":   "⛅",
	"partly-cloudy-night": "🌙",
	"rain":                "☔",
	"sleet":               "❄ ☔",
	"snow":                "❄",
	"wind":                "🍃",
	"error":               "❗",
}

var maxCacheAge, _ = time.ParseDuration("1h")

var coordinates string
var tempFormat string
var key string
var tmpDir string
var zipCode string

func init() {
	flag.StringVar(&coordinates, "coordinates", "", "the coordinates, expressed as latitude,longitude")
	flag.StringVar(&tempFormat, "temp", "", "display temperature in c or f")
	flag.StringVar(&key, "key", os.Getenv("FORECAST_IO_API_KEY"), "your forecast.io API key")
	flag.StringVar(&tmpDir, "tmpdir", os.TempDir(), "the directory to use to store cached responses")
	flag.StringVar(&zipCode, "zipcode", "", "a USPS ZIP Code")

	flag.Parse()
}

func main() {
	if key == "" {
		exitWith("Please provide your forecast.io API key with -key, or set FORECAST_IO_API_KEY", 1)
	}

	if tempFormat != "" && tempFormat != "c" && tempFormat != "f" {
		exitWith("The -temp argument must be 'c' or 'f'", 1)
	}

	if coordinates == "" && zipCode == "" {
		exitWith("Please provide a -zipcode or -coordinates", 1)
	}

	if zipCode != "" {
		coord, err := zipcode.Lookup(zipCode)
		check(err)
		coordinates = coord.String()
	}

	latitude, longitude := formatCoordinates(coordinates)

	cacheFilename := fmt.Sprintf("emoji-weather-%s-%s.json", latitude, longitude)
	cacheFile := path.Join(tmpDir, cacheFilename)

	var err error
	var json []byte

	if isCacheStale(cacheFile) {
		json, err = getForecast(key, latitude, longitude)
		check(err)

		err = writeCache(cacheFile, json)
		check(err)
	} else {
		json, err = ioutil.ReadFile(cacheFile)
		check(err)
	}

	fmt.Println(formatConditions(extractConditionFromJSON(json)))
}

func formatCoordinates(coordinates string) (latitude string, longitude string) {
	coordinateParts := strings.Split(coordinates, ",")

	if len(coordinateParts) != 2 {
		exitWith("You must specify latitude and longitude like so: 39.95,-75.16", 1)
	}

	latitude = roundCoordinatePart(coordinateParts[0])
	longitude = roundCoordinatePart(coordinateParts[1])

	return
}

func roundCoordinatePart(part string) string {
	float, err := strconv.ParseFloat(part, 32)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%.2f", float)
}

func isCacheStale(cacheFile string) bool {
	stat, err := os.Stat(cacheFile)

	return os.IsNotExist(err) || time.Since(stat.ModTime()) > maxCacheAge
}

func getForecast(key string, latitude string, longitude string) (json []byte, err error) {
	res, err := forecast.GetResponse(key, latitude, longitude, "now", "us")
	if err != nil {
		return nil, err
	}

	json, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	return json, nil
}

func writeCache(cacheFile string, json []byte) (err error) {
	return ioutil.WriteFile(cacheFile, json, 0644)
}

func formatConditions(condition string, temperature float64) (result string) {
	result, ok := conditionIcons[condition]
	if !ok {
		result = condition
	}
	if tempFormat == "c" {
		temperature = convertToCelcius(temperature)
		result = fmt.Sprintf("%s %.1f°", result, temperature)
	}
	if tempFormat == "f" {
		result = fmt.Sprintf("%s %.0f°", result, temperature)
	}
	return
}

func convertToCelcius(temperature float64) float64 {
	return (temperature - 32) * 5 / 9
}

func extractConditionFromJSON(jsonBlob []byte) (condition string, temperature float64) {
	f, err := forecast.FromJSON(jsonBlob)

	if err != nil {
		return "error", 0.0
	}

	if f.Code > 0 {
		return "error", 0.0
	}

	return f.Currently.Icon, f.Currently.Temperature
}

func exitWith(message interface{}, status int) {
	fmt.Printf("❗\n❗\n%s\n", message)
	os.Exit(status)
}

func check(err error) {
	if err != nil {
		exitWith(err, 1)
	}
}
<commit_msg>Updated to work with forecast V2. Changed units/added language params to forecast.GetResponse. Added bytes library to convert []byte to io.Reader for forecast.FromJSON call<commit_after>package main

import (
        "bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/justincampbell/zipcode"
	"github.com/mlbright/forecast/v2"
)

var conditionIcons = map[string]string{
	"clear-day":           "☀",
	"clear-night":         "🌙",
	"cloudy":              "☁",
	"fog":                 "🌁",
	"partly-cloudy-day":   "⛅",
	"partly-cloudy-night": "🌙",
	"rain":                "☔",
	"sleet":               "❄ ☔",
	"snow":                "❄",
	"wind":                "🍃",
	"error":               "❗",
}

var maxCacheAge, _ = time.ParseDuration("1h")

var coordinates string
var tempFormat string
var key string
var tmpDir string
var zipCode string

func init() {
	flag.StringVar(&coordinates, "coordinates", "", "the coordinates, expressed as latitude,longitude")
	flag.StringVar(&tempFormat, "temp", "", "display temperature in c or f")
	flag.StringVar(&key, "key", os.Getenv("FORECAST_IO_API_KEY"), "your forecast.io API key")
	flag.StringVar(&tmpDir, "tmpdir", os.TempDir(), "the directory to use to store cached responses")
	flag.StringVar(&zipCode, "zipcode", "", "a USPS ZIP Code")

	flag.Parse()
}

func main() {
	if key == "" {
		exitWith("Please provide your forecast.io API key with -key, or set FORECAST_IO_API_KEY", 1)
	}

	if tempFormat != "" && tempFormat != "c" && tempFormat != "f" {
		exitWith("The -temp argument must be 'c' or 'f'", 1)
	}

	if coordinates == "" && zipCode == "" {
		exitWith("Please provide a -zipcode or -coordinates", 1)
	}

	if zipCode != "" {
		coord, err := zipcode.Lookup(zipCode)
		check(err)
		coordinates = coord.String()
	}

	latitude, longitude := formatCoordinates(coordinates)

	cacheFilename := fmt.Sprintf("emoji-weather-%s-%s.json", latitude, longitude)
	cacheFile := path.Join(tmpDir, cacheFilename)

	var err error
	var json []byte

	if isCacheStale(cacheFile) {
		json, err = getForecast(key, latitude, longitude)
		check(err)

		err = writeCache(cacheFile, json)
		check(err)
	} else {
		json, err = ioutil.ReadFile(cacheFile)
		check(err)
	}

	fmt.Println(formatConditions(extractConditionFromJSON(json)))
}

func formatCoordinates(coordinates string) (latitude string, longitude string) {
	coordinateParts := strings.Split(coordinates, ",")

	if len(coordinateParts) != 2 {
		exitWith("You must specify latitude and longitude like so: 39.95,-75.16", 1)
	}

	latitude = roundCoordinatePart(coordinateParts[0])
	longitude = roundCoordinatePart(coordinateParts[1])

	return
}

func roundCoordinatePart(part string) string {
	float, err := strconv.ParseFloat(part, 32)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%.2f", float)
}

func isCacheStale(cacheFile string) bool {
	stat, err := os.Stat(cacheFile)

	return os.IsNotExist(err) || time.Since(stat.ModTime()) > maxCacheAge
}

func getForecast(key string, latitude string, longitude string) (json []byte, err error) {
	res, err := forecast.GetResponse(key, latitude, longitude, "now", forecast.US, forecast.English)
	if err != nil {
		return nil, err
	}

	json, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	return json, nil
}

func writeCache(cacheFile string, json []byte) (err error) {
	return ioutil.WriteFile(cacheFile, json, 0644)
}

func formatConditions(condition string, temperature float64) (result string) {
	result, ok := conditionIcons[condition]
	if !ok {
		result = condition
	}
	if tempFormat == "c" {
		temperature = convertToCelcius(temperature)
		result = fmt.Sprintf("%s %.1f°", result, temperature)
	}
	if tempFormat == "f" {
		result = fmt.Sprintf("%s %.0f°", result, temperature)
	}
	return
}

func convertToCelcius(temperature float64) float64 {
	return (temperature - 32) * 5 / 9
}

func extractConditionFromJSON(jsonBlob []byte) (condition string, temperature float64) {
	f, err := forecast.FromJSON(bytes.NewReader(jsonBlob))

	if err != nil {
		return "error", 0.0
	}

	if f.Code > 0 {
		return "error", 0.0
	}

	return f.Currently.Icon, f.Currently.Temperature
}

func exitWith(message interface{}, status int) {
	fmt.Printf("❗\n❗\n%s\n", message)
	os.Exit(status)
}

func check(err error) {
	if err != nil {
		exitWith(err, 1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package errors

import (
	"fmt"
)

// NotFoundError records an error when something has not been found.
type NotFoundError struct {
	// error is the underlying error.
	error
	Msg string
}

// IsNotFoundError returns true if err is a NotFoundError.
func IsNotFoundError(err error) bool {
	if _, ok := err.(*NotFoundError); ok {
		return true
	}
	return false
}

func (e *NotFoundError) Error() string {
	if e.Msg != "" || e.error == nil {
		if e.error != nil {
			return fmt.Sprintf("%s: %v", e.Msg, e.error.Error())
		}
		return e.Msg
	}
	return e.error.Error()
}

// NotFoundf returns a error for which IsNotFound returns
// true. The message for the error is made up from the given
// arguments formatted as with fmt.Sprintf, with the
// string " not found" appended.
func NotFoundf(format string, args ...interface{}) error {
	return &NotFoundError{nil, fmt.Sprintf(format+" not found", args...)}
}

// UnauthorizedError represents the error that an operation is unauthorized.
// Use IsUnauthorized() to determine if the error was related to authorization failure.
type UnauthorizedError struct {
	error
	Msg string
}

func IsUnauthorizedError(err error) bool {
	_, ok := err.(*UnauthorizedError)
	return ok
}

func (e *UnauthorizedError) Error() string {
	if e.error != nil {
		return fmt.Sprintf("%s: %v", e.Msg, e.error.Error())
	}
	return e.Msg
}

// Unauthorizedf returns an error for which IsUnauthorizedError returns true.
// It is mainly used for testing.
func Unauthorizedf(format string, args ...interface{}) error {
	return &UnauthorizedError{nil, fmt.Sprintf(format, args...)}
}

// NoEnvError indicates the default environment config file is missing
type NoEnvError struct {
	error
}

// IsNoEnv returns true if err is a NoEnvError
func IsNoEnv(err error) bool {
	_, ok := err.(NoEnvError)
	return ok
}
<commit_msg>fix a couple comments<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package errors

import (
	"fmt"
)

// NotFoundError records an error when something has not been found.
type NotFoundError struct {
	// error is the underlying error.
	error
	Msg string
}

// IsNotFoundError returns true if err is a NotFoundError.
func IsNotFoundError(err error) bool {
	if _, ok := err.(*NotFoundError); ok {
		return true
	}
	return false
}

func (e *NotFoundError) Error() string {
	if e.Msg != "" || e.error == nil {
		if e.error != nil {
			return fmt.Sprintf("%s: %v", e.Msg, e.error.Error())
		}
		return e.Msg
	}
	return e.error.Error()
}

// NotFoundf returns a error for which IsNotFound returns
// true. The message for the error is made up from the given
// arguments formatted as with fmt.Sprintf, with the
// string " not found" appended.
func NotFoundf(format string, args ...interface{}) error {
	return &NotFoundError{nil, fmt.Sprintf(format+" not found", args...)}
}

// UnauthorizedError represents the error that an operation is unauthorized.
// Use IsUnauthorized() to determine if the error was related to authorization failure.
type UnauthorizedError struct {
	error
	Msg string
}

func IsUnauthorizedError(err error) bool {
	_, ok := err.(*UnauthorizedError)
	return ok
}

func (e *UnauthorizedError) Error() string {
	if e.error != nil {
		return fmt.Sprintf("%s: %v", e.Msg, e.error.Error())
	}
	return e.Msg
}

// Unauthorizedf returns an error for which IsUnauthorizedError returns true.
// It is mainly used for testing.
func Unauthorizedf(format string, args ...interface{}) error {
	return &UnauthorizedError{nil, fmt.Sprintf(format, args...)}
}

// NoEnvError indicates the default environment config file is missing.
type NoEnvError struct {
	error
}

// IsNoEnv returns if err is a NoEnvError.
func IsNoEnv(err error) bool {
	_, ok := err.(NoEnvError)
	return ok
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin 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 errors defines the error handling used by all Upspin software.
package errors

import (
	"bytes"
	"fmt"
	"runtime"
	"strings"

	"upspin.io/log"
	"upspin.io/upspin"
)

// Error is the type that implements the error interface.
// It contains a number of fields, each of different type.
// An Error value may leave some values unset.
type Error struct {
	// Path is the Upspin path name of the item being accessed.
	Path upspin.PathName
	// User is the Upspin name of the user attempting the operation.
	User upspin.UserName
	// Op is the operation being performed, usually the method
	// being invoked (Get, Put, etc.)
	Op string
	// Kind is the class of error, such as permission failure,
	// or "Other" if its class is unknown or irrelevant.
	Kind Kind
	// The underlying error that triggered this one, if any.
	Err error
}

var _ error = (*Error)(nil)

// Kind defines the kind of error this is, mostly for use by systems
// such as FUSE that must act differently depending on the error.
type Kind uint8

const (
	Other      Kind = iota // Unclassified error. This value is not printed in the error message.
	Invalid                // Invalid operation for this type of item.
	Permission             // Permission denied.
	Syntax                 // Ill-formed argument such as invalid file name.
	IO                     // External I/O error such as network failure.
	Exist                  // Item exists but should not.
	NotExist               // Item does not exist.
)

func (k Kind) String() string {
	switch k {
	case Invalid:
		return "invalid operation"
	case Permission:
		return "permission denied"
	case Syntax:
		return "syntax error"
	case IO:
		return "I/O error"
	case Exist:
		return "item already exists"
	case NotExist:
		return "item does not exist"
	case Other:
		return "other error"
	}
	return "unknown error kind"
}

// E builds an error value from its arguments.
// The type of each argument determines its meaning.
// If more than one argument of a given type is presented,
// only the last one is recorded.
//
// The types are:
//	upspin.PathName
//		The Upspin path name of the item being accessed.
//	upspin.UserName
//		The Upspin name of the user attempting the operation.
//	string
//		The operation being performed, usually the method
//		being invoked (Get, Put, etc.)
//	errors.Kind
//		The class of error, such as permission failure.
//	error
//		The underlying error that triggered this one.
//
// If the error is printed, only those items that have been
// set to non-zero values will appear in the result.
//
func E(args ...interface{}) error {
	if len(args) == 0 {
		return nil
	}
	e := &Error{}
	for _, arg := range args {
		switch arg := arg.(type) {
		case upspin.PathName:
			e.Path = arg
		case upspin.UserName:
			e.User = arg
		case string:
			// Someone might accidentally call us with a user or path name
			// that is not of the right type. Take care of that and log it.
			if strings.Contains(arg, "@") {
				_, file, line, _ := runtime.Caller(1)
				log.Printf("errors.E: unqualified type for %q from %s:%d", arg, file, line)
				if strings.Contains(arg, "/") {
					e.Path = upspin.PathName(arg)
				} else {
					e.User = upspin.UserName(arg)
				}
				continue
			}
			e.Op = arg
		case Kind:
			e.Kind = arg
		case error:
			e.Err = arg
		default:
			_, file, line, _ := runtime.Caller(1)
			log.Printf("errors.E: bad call from %s:%d: %v", file, line, args)
			return fmt.Errorf("unknown type %T, value %v in error call", arg, arg)
		}
	}
	return e
}

// pad appends str to the buffer if the buffer already has some data.
func pad(b *bytes.Buffer, str string) {
	if b.Len() == 0 {
		return
	}
	b.WriteString(str)
}

func (e *Error) Error() string {
	b := new(bytes.Buffer)
	if e.Path != "" {
		b.WriteString(string(e.Path))
	}
	if e.User != "" {
		pad(b, ", ")
		b.WriteString("for ")
		b.WriteString(string(e.User))
	}
	if e.Op != "" {
		pad(b, ": ")
		b.WriteString(e.Op)
	}
	if e.Kind != 0 {
		pad(b, ": ")
		b.WriteString(e.Kind.String())
	}
	if e.Err != nil {
		// Indent on new line if we are cascading Upspin errors.
		if _, ok := e.Err.(*Error); ok {
			pad(b, ":\n\t")
		} else {
			pad(b, ": ")
		}
		b.WriteString(e.Err.Error())
	}
	if b.Len() == 0 {
		return "no error"
	}
	return b.String()
}
<commit_msg>errors: do not overwrite a valid path or user in the fallback string case<commit_after>// Copyright 2016 The Upspin 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 errors defines the error handling used by all Upspin software.
package errors

import (
	"bytes"
	"fmt"
	"runtime"
	"strings"

	"upspin.io/log"
	"upspin.io/upspin"
)

// Error is the type that implements the error interface.
// It contains a number of fields, each of different type.
// An Error value may leave some values unset.
type Error struct {
	// Path is the Upspin path name of the item being accessed.
	Path upspin.PathName
	// User is the Upspin name of the user attempting the operation.
	User upspin.UserName
	// Op is the operation being performed, usually the name of the method
	// being invoked (Get, Put, etc.). It should not contain an at sign @.
	Op string
	// Kind is the class of error, such as permission failure,
	// or "Other" if its class is unknown or irrelevant.
	Kind Kind
	// The underlying error that triggered this one, if any.
	Err error
}

var _ error = (*Error)(nil)

// Kind defines the kind of error this is, mostly for use by systems
// such as FUSE that must act differently depending on the error.
type Kind uint8

const (
	Other      Kind = iota // Unclassified error. This value is not printed in the error message.
	Invalid                // Invalid operation for this type of item.
	Permission             // Permission denied.
	Syntax                 // Ill-formed argument such as invalid file name.
	IO                     // External I/O error such as network failure.
	Exist                  // Item exists but should not.
	NotExist               // Item does not exist.
)

func (k Kind) String() string {
	switch k {
	case Invalid:
		return "invalid operation"
	case Permission:
		return "permission denied"
	case Syntax:
		return "syntax error"
	case IO:
		return "I/O error"
	case Exist:
		return "item already exists"
	case NotExist:
		return "item does not exist"
	case Other:
		return "other error"
	}
	return "unknown error kind"
}

// E builds an error value from its arguments.
// The type of each argument determines its meaning.
// If more than one argument of a given type is presented,
// only the last one is recorded.
//
// The types are:
//	upspin.PathName
//		The Upspin path name of the item being accessed.
//	upspin.UserName
//		The Upspin name of the user attempting the operation.
//	string
//		The operation being performed, usually the method
//		being invoked (Get, Put, etc.)
//	errors.Kind
//		The class of error, such as permission failure.
//	error
//		The underlying error that triggered this one.
//
// If the error is printed, only those items that have been
// set to non-zero values will appear in the result.
//
func E(args ...interface{}) error {
	if len(args) == 0 {
		return nil
	}
	e := &Error{}
	for _, arg := range args {
		switch arg := arg.(type) {
		case upspin.PathName:
			e.Path = arg
		case upspin.UserName:
			e.User = arg
		case string:
			// Someone might accidentally call us with a user or path name
			// that is not of the right type. Take care of that and log it.
			if strings.Contains(arg, "@") {
				_, file, line, _ := runtime.Caller(1)
				log.Printf("errors.E: unqualified type for %q from %s:%d", arg, file, line)
				if strings.Contains(arg, "/") {
					if e.Path != "" { // Don't overwrite a valid path.
						e.Path = upspin.PathName(arg)
					}
				} else {
					if e.User != "" { // Don't overwrite a valid user.
						e.User = upspin.UserName(arg)
					}
				}
				continue
			}
			e.Op = arg
		case Kind:
			e.Kind = arg
		case error:
			e.Err = arg
		default:
			_, file, line, _ := runtime.Caller(1)
			log.Printf("errors.E: bad call from %s:%d: %v", file, line, args)
			return fmt.Errorf("unknown type %T, value %v in error call", arg, arg)
		}
	}
	return e
}

// pad appends str to the buffer if the buffer already has some data.
func pad(b *bytes.Buffer, str string) {
	if b.Len() == 0 {
		return
	}
	b.WriteString(str)
}

func (e *Error) Error() string {
	b := new(bytes.Buffer)
	if e.Path != "" {
		b.WriteString(string(e.Path))
	}
	if e.User != "" {
		pad(b, ", ")
		b.WriteString("for ")
		b.WriteString(string(e.User))
	}
	if e.Op != "" {
		pad(b, ": ")
		b.WriteString(e.Op)
	}
	if e.Kind != 0 {
		pad(b, ": ")
		b.WriteString(e.Kind.String())
	}
	if e.Err != nil {
		// Indent on new line if we are cascading Upspin errors.
		if _, ok := e.Err.(*Error); ok {
			pad(b, ":\n\t")
		} else {
			pad(b, ": ")
		}
		b.WriteString(e.Err.Error())
	}
	if b.Len() == 0 {
		return "no error"
	}
	return b.String()
}
<|endoftext|>