<commit_before>package mountinfo

import (
	"errors"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"testing"

	"golang.org/x/sys/unix"
)

// tMount is a wrapper for unix.Mount which is used to prepare test cases.
// It skips the test case if mounting is not possible (i.e. user is not root),
// adds more context to mount error, if any, and installs a cleanup handler to
// undo the mount.
func tMount(t *testing.T, src, dst, fstype string, flags uintptr, options string) error {
	if os.Getuid() != 0 {
		t.Skip("root required for mounting")
	}

	err := unix.Mount(src, dst, fstype, flags, options)
	if err != nil {
		return &os.PathError{Path: dst, Op: "mount", Err: err}
	}
	t.Cleanup(func() {
		if err := unix.Unmount(dst, unix.MNT_DETACH); err != nil {
			t.Errorf("cleanup: unmount %q failed: %v", dst, err)
		}
	})
	return nil
}

var testMounts = []struct {
	desc       string
	isNotExist bool
	isMount    bool
	isBind     bool
	// prepare returns a path that needs to be checked, and the error, if any.
	//
	// It is responsible for cleanup (by using t.Cleanup).
	//
	// It should not fail the test (i.e. no calls to t.Error/t.Fatal).
	// The only exception to this rule is some cases use t.TempDir() for
	// simplicity (no need to check for errors or call t.Cleanup()), and
	// it may call t.Fatal, but practically we don't expect it.
	prepare func(t *testing.T) (string, error)
}{
	{
		desc:       "non-existent path",
		isNotExist: true,
		prepare: func(t *testing.T) (string, error) {
			return "/non/existent/path", nil
		},
	},
	{
		desc: "not mounted directory",
		prepare: func(t *testing.T) (dir string, err error) {
			dir = t.TempDir()
			return dir, err
		},
	},
	{
		desc:    "tmpfs mount",
		isMount: true,
		prepare: func(t *testing.T) (mnt string, err error) {
			mnt = t.TempDir()
			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			return mnt, err
		},
	},
	{
		desc:    "tmpfs mount ending with a slash",
		isMount: true,
		prepare: func(t *testing.T) (mnt string, err error) {
			mnt = t.TempDir() + "/"
			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			return mnt, err
		},
	},
	{
		desc:       "broken symlink",
		isNotExist: true,
		prepare: func(t *testing.T) (link string, err error) {
			dir := t.TempDir()
			link = filepath.Join(dir, "broken-symlink")
			err = os.Symlink("/some/non/existent/dest", link)
			return link, err
		},
	},
	{
		desc: "symlink to not mounted directory",
		prepare: func(t *testing.T) (link string, err error) {
			tmp := t.TempDir()

			dir, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			link = filepath.Join(tmp, "symlink")
			err = os.Symlink(dir, link)

			return link, err
		},
	},
	{
		desc:    "symlink to mounted directory",
		isMount: true,
		prepare: func(t *testing.T) (link string, err error) {
			tmp := t.TempDir()

			dir, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			err = tMount(t, "tmpfs", dir, "tmpfs", 0, "")
			if err != nil {
				return
			}

			link = filepath.Join(tmp, "symlink")
			err = os.Symlink(dir, link)

			return link, err
		},
	},
	{
		desc:    "symlink to a file on a different filesystem",
		isMount: false,
		prepare: func(t *testing.T) (link string, err error) {
			tmp := t.TempDir()

			mnt, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			if err != nil {
				return
			}
			file, err := os.CreateTemp(mnt, "file")
			if err != nil {
				return
			}
			file.Close()
			link = filepath.Join(tmp, "link")
			err = os.Symlink(file.Name(), link)

			return link, err
		},
	},
	{
		desc:    "path whose parent is a symlink to directory on another device",
		isMount: false,
		prepare: func(t *testing.T) (path string, err error) {
			tmp := t.TempDir()

			mnt, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			if err != nil {
				return
			}
			file, err := os.CreateTemp(mnt, "file")
			if err != nil {
				return
			}
			file.Close()

			// Create link -> mnt under tmp dir.
			link := filepath.Join(tmp, "link")
			err = os.Symlink(filepath.Base(mnt), link)
			// Path to check is /<tmp>/link/file.
			path = filepath.Join(link, filepath.Base(file.Name()))

			return path, err
		},
	},
	{
		desc:    "directory bind mounted to itself",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (mnt string, err error) {
			mnt = t.TempDir()
			err = tMount(t, mnt, mnt, "", unix.MS_BIND, "")
			return mnt, err
		},
	},
	{
		desc:    "directory bind-mounted to other directory",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (mnt string, err error) {
			dir := t.TempDir()
			mnt = t.TempDir()
			err = tMount(t, dir, mnt, "", unix.MS_BIND, "")
			return mnt, err
		},
	},
	{
		desc: "not mounted file",
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()
			file, err := os.CreateTemp(dir, "file")
			if err != nil {
				return
			}
			return file.Name(), err
		},
	},
	{
		desc:    "regular file bind-mounted to itself",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()

			file, err := os.CreateTemp(dir, "file")
			if err != nil {
				return
			}
			file.Close()
			path = file.Name()

			err = tMount(t, path, path, "", unix.MS_BIND, "")

			return path, err
		},
	},
	{
		desc: "not mounted socket",
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()
			path = filepath.Join(dir, "sock")
			_, err = net.Listen("unix", path)
			return path, err
		},
	},
	{
		desc:    "socket bind-mounted to itself",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()
			path = filepath.Join(dir, "sock")
			_, err = net.Listen("unix", path)
			if err != nil {
				return
			}
			err = tMount(t, path, path, "", unix.MS_BIND, "")

			return path, err
		},
	},
}

func requireOpenat2(t *testing.T) {
	t.Helper()
	if err := tryOpenat2(); err != nil {
		t.Skipf("openat2: %v (old kernel? need Linux 5.6+)", err)
	}
}

func tryOpenat2() error {
	fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{Flags: unix.O_RDONLY})
	if err == nil {
		_ = unix.Close(fd)
	}
	return err
}

func TestMountedBy(t *testing.T) {
	checked := false

	// List of individual implementations to check.
	toCheck := []func(string) (bool, error){mountedByMountinfo, mountedByStat}
	if tryOpenat2() == nil {
		toCheck = append(toCheck, mountedByOpenat2)
	}

	for _, tc := range testMounts {
		tc := tc
		t.Run(tc.desc, func(t *testing.T) {
			m, err := tc.prepare(t)
			if err != nil {
				t.Fatalf("prepare: %v", err)
			}

			exp := tc.isMount

			// Check the public Mounted() function as a whole.
			mounted, err := Mounted(m)
			if err == nil {
				if mounted != exp {
					t.Errorf("Mounted: expected %v, got %v", exp, mounted)
				}
			} else {
				// Got an error; is it expected?
				if !(tc.isNotExist && errors.Is(err, os.ErrNotExist)) {
					t.Errorf("Mounted: unexpected error: %v", err)
				}
				// Check false is returned in error case.
				if mounted {
					t.Error("Mounted: expected false on error")
				}
			}

			// Check individual mountedBy* implementations.

			// All mountedBy* functions should be called with normalized paths.
			m, err = normalizePath(m)
			if err != nil {
				if tc.isNotExist && errors.Is(err, os.ErrNotExist) {
					return
				}
				t.Fatalf("normalizePath: %v", err)
			}

			for _, fn := range toCheck {
				// Figure out function name.
				name := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()

				mounted, err = fn(m)
				if err != nil {
					t.Errorf("%s: %v", name, err)
					// Check false is returned in error case.
					if mounted {
						t.Errorf("%s: expected false on error", name)
					}
				} else if mounted != exp {
					if tc.isBind && strings.HasSuffix(name, "mountedByStat") {
						// mountedByStat can not detect bind mounts.
					} else {
						t.Errorf("%s: expected %v, got %v", name, exp, mounted)
					}
				}
				checked = true
			}
		})
	}

	if !checked {
		t.Skip("no mounts to check")
	}
}

func TestMountedByOpenat2VsMountinfo(t *testing.T) {
	requireOpenat2(t)

	mounts, err := GetMounts(nil)
	if err != nil {
		t.Fatalf("GetMounts error: %v", err)
	}

	for _, mount := range mounts {
		m := mount.Mountpoint
		if m == "/" {
			// mountedBy*() won't work for /, so skip it
			// (this special case is handled by Mounted).
			continue
		}
		mounted, err := mountedByOpenat2(m)
		if err != nil {
			if !errors.Is(err, os.ErrPermission) {
				t.Errorf("mountedByOpenat2(%q) error: %+v", m, err)
			}
		} else if !mounted {
			t.Errorf("mountedByOpenat2(%q): expected true, got false", m)
		}
	}
}
<commit_msg>mountinfo: TestMountedBy: rm exp<commit_after>package mountinfo

import (
	"errors"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"testing"

	"golang.org/x/sys/unix"
)

// tMount is a wrapper for unix.Mount which is used to prepare test cases.
// It skips the test case if mounting is not possible (i.e. user is not root),
// adds more context to mount error, if any, and installs a cleanup handler to
// undo the mount.
func tMount(t *testing.T, src, dst, fstype string, flags uintptr, options string) error {
	if os.Getuid() != 0 {
		t.Skip("root required for mounting")
	}

	err := unix.Mount(src, dst, fstype, flags, options)
	if err != nil {
		return &os.PathError{Path: dst, Op: "mount", Err: err}
	}
	t.Cleanup(func() {
		if err := unix.Unmount(dst, unix.MNT_DETACH); err != nil {
			t.Errorf("cleanup: unmount %q failed: %v", dst, err)
		}
	})
	return nil
}

var testMounts = []struct {
	desc       string
	isNotExist bool
	isMount    bool
	isBind     bool
	// prepare returns a path that needs to be checked, and the error, if any.
	//
	// It is responsible for cleanup (by using t.Cleanup).
	//
	// It should not fail the test (i.e. no calls to t.Error/t.Fatal).
	// The only exception to this rule is some cases use t.TempDir() for
	// simplicity (no need to check for errors or call t.Cleanup()), and
	// it may call t.Fatal, but practically we don't expect it.
	prepare func(t *testing.T) (string, error)
}{
	{
		desc:       "non-existent path",
		isNotExist: true,
		prepare: func(t *testing.T) (string, error) {
			return "/non/existent/path", nil
		},
	},
	{
		desc: "not mounted directory",
		prepare: func(t *testing.T) (dir string, err error) {
			dir = t.TempDir()
			return dir, err
		},
	},
	{
		desc:    "tmpfs mount",
		isMount: true,
		prepare: func(t *testing.T) (mnt string, err error) {
			mnt = t.TempDir()
			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			return mnt, err
		},
	},
	{
		desc:    "tmpfs mount ending with a slash",
		isMount: true,
		prepare: func(t *testing.T) (mnt string, err error) {
			mnt = t.TempDir() + "/"
			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			return mnt, err
		},
	},
	{
		desc:       "broken symlink",
		isNotExist: true,
		prepare: func(t *testing.T) (link string, err error) {
			dir := t.TempDir()
			link = filepath.Join(dir, "broken-symlink")
			err = os.Symlink("/some/non/existent/dest", link)
			return link, err
		},
	},
	{
		desc: "symlink to not mounted directory",
		prepare: func(t *testing.T) (link string, err error) {
			tmp := t.TempDir()

			dir, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			link = filepath.Join(tmp, "symlink")
			err = os.Symlink(dir, link)

			return link, err
		},
	},
	{
		desc:    "symlink to mounted directory",
		isMount: true,
		prepare: func(t *testing.T) (link string, err error) {
			tmp := t.TempDir()

			dir, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			err = tMount(t, "tmpfs", dir, "tmpfs", 0, "")
			if err != nil {
				return
			}

			link = filepath.Join(tmp, "symlink")
			err = os.Symlink(dir, link)

			return link, err
		},
	},
	{
		desc:    "symlink to a file on a different filesystem",
		isMount: false,
		prepare: func(t *testing.T) (link string, err error) {
			tmp := t.TempDir()

			mnt, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			if err != nil {
				return
			}
			file, err := os.CreateTemp(mnt, "file")
			if err != nil {
				return
			}
			file.Close()
			link = filepath.Join(tmp, "link")
			err = os.Symlink(file.Name(), link)

			return link, err
		},
	},
	{
		desc:    "path whose parent is a symlink to directory on another device",
		isMount: false,
		prepare: func(t *testing.T) (path string, err error) {
			tmp := t.TempDir()

			mnt, err := os.MkdirTemp(tmp, "dir")
			if err != nil {
				return
			}

			err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "")
			if err != nil {
				return
			}
			file, err := os.CreateTemp(mnt, "file")
			if err != nil {
				return
			}
			file.Close()

			// Create link -> mnt under tmp dir.
			link := filepath.Join(tmp, "link")
			err = os.Symlink(filepath.Base(mnt), link)
			// Path to check is /<tmp>/link/file.
			path = filepath.Join(link, filepath.Base(file.Name()))

			return path, err
		},
	},
	{
		desc:    "directory bind mounted to itself",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (mnt string, err error) {
			mnt = t.TempDir()
			err = tMount(t, mnt, mnt, "", unix.MS_BIND, "")
			return mnt, err
		},
	},
	{
		desc:    "directory bind-mounted to other directory",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (mnt string, err error) {
			dir := t.TempDir()
			mnt = t.TempDir()
			err = tMount(t, dir, mnt, "", unix.MS_BIND, "")
			return mnt, err
		},
	},
	{
		desc: "not mounted file",
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()
			file, err := os.CreateTemp(dir, "file")
			if err != nil {
				return
			}
			return file.Name(), err
		},
	},
	{
		desc:    "regular file bind-mounted to itself",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()

			file, err := os.CreateTemp(dir, "file")
			if err != nil {
				return
			}
			file.Close()
			path = file.Name()

			err = tMount(t, path, path, "", unix.MS_BIND, "")

			return path, err
		},
	},
	{
		desc: "not mounted socket",
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()
			path = filepath.Join(dir, "sock")
			_, err = net.Listen("unix", path)
			return path, err
		},
	},
	{
		desc:    "socket bind-mounted to itself",
		isMount: true,
		isBind:  true,
		prepare: func(t *testing.T) (path string, err error) {
			dir := t.TempDir()
			path = filepath.Join(dir, "sock")
			_, err = net.Listen("unix", path)
			if err != nil {
				return
			}
			err = tMount(t, path, path, "", unix.MS_BIND, "")

			return path, err
		},
	},
}

func requireOpenat2(t *testing.T) {
	t.Helper()
	if err := tryOpenat2(); err != nil {
		t.Skipf("openat2: %v (old kernel? need Linux 5.6+)", err)
	}
}

func tryOpenat2() error {
	fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{Flags: unix.O_RDONLY})
	if err == nil {
		_ = unix.Close(fd)
	}
	return err
}

func TestMountedBy(t *testing.T) {
	checked := false

	// List of individual implementations to check.
	toCheck := []func(string) (bool, error){mountedByMountinfo, mountedByStat}
	if tryOpenat2() == nil {
		toCheck = append(toCheck, mountedByOpenat2)
	}

	for _, tc := range testMounts {
		tc := tc
		t.Run(tc.desc, func(t *testing.T) {
			m, err := tc.prepare(t)
			if err != nil {
				t.Fatalf("prepare: %v", err)
			}

			// Check the public Mounted() function as a whole.
			mounted, err := Mounted(m)
			if err == nil {
				if mounted != tc.isMount {
					t.Errorf("Mounted: expected %v, got %v", tc.isMount, mounted)
				}
			} else {
				// Got an error; is it expected?
				if !(tc.isNotExist && errors.Is(err, os.ErrNotExist)) {
					t.Errorf("Mounted: unexpected error: %v", err)
				}
				// Check false is returned in error case.
				if mounted {
					t.Error("Mounted: expected false on error")
				}
			}

			// Check individual mountedBy* implementations.

			// All mountedBy* functions should be called with normalized paths.
			m, err = normalizePath(m)
			if err != nil {
				if tc.isNotExist && errors.Is(err, os.ErrNotExist) {
					return
				}
				t.Fatalf("normalizePath: %v", err)
			}

			for _, fn := range toCheck {
				// Figure out function name.
				name := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()

				mounted, err = fn(m)
				if err != nil {
					t.Errorf("%s: %v", name, err)
					// Check false is returned in error case.
					if mounted {
						t.Errorf("%s: expected false on error", name)
					}
				} else if mounted != tc.isMount {
					if tc.isBind && strings.HasSuffix(name, "mountedByStat") {
						// mountedByStat can not detect bind mounts.
					} else {
						t.Errorf("%s: expected %v, got %v", name, tc.isMount, mounted)
					}
				}
				checked = true
			}
		})
	}

	if !checked {
		t.Skip("no mounts to check")
	}
}

func TestMountedByOpenat2VsMountinfo(t *testing.T) {
	requireOpenat2(t)

	mounts, err := GetMounts(nil)
	if err != nil {
		t.Fatalf("GetMounts error: %v", err)
	}

	for _, mount := range mounts {
		m := mount.Mountpoint
		if m == "/" {
			// mountedBy*() won't work for /, so skip it
			// (this special case is handled by Mounted).
			continue
		}
		mounted, err := mountedByOpenat2(m)
		if err != nil {
			if !errors.Is(err, os.ErrPermission) {
				t.Errorf("mountedByOpenat2(%q) error: %+v", m, err)
			}
		} else if !mounted {
			t.Errorf("mountedByOpenat2(%q): expected true, got false", m)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2021 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build ignore
// +build ignore

package main

import (
	"bytes"
	"fmt"
	"go/format"
	"log"
	"math"
	"os"
	"runtime"
	"runtime/metrics"
	"strconv"
	"strings"
	"text/template"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/internal"
)

func main() {
	if len(os.Args) != 2 {
		log.Fatal("requires Go version (e.g. go1.17) as an argument")
	}
	toolVersion := runtime.Version()
<<<<<<< HEAD
	if majorVersion := toolVersion[:strings.LastIndexByte(toolVersion, '.')]; majorVersion != os.Args[1] {
		log.Fatalf("using Go version %q but expected Go version %q", majorVersion, os.Args[1])
=======
	mtv := majorVersion(toolVersion)
	mv := majorVersion(os.Args[1])
	if mtv != mv {
		log.Fatalf("using Go version %q but expected Go version %q", mtv, mv)
>>>>>>> f251146 (prometheus: Fix convention violating names for generated collector metrics (#1048))
	}
	version, err := parseVersion(mv)
	if err != nil {
		log.Fatalf("parsing Go version: %v", err)
	}

	// Generate code.
	var buf bytes.Buffer
	err = testFile.Execute(&buf, struct {
		Descriptions []metrics.Description
		GoVersion    goVersion
		Cardinality  int
	}{
		Descriptions: metrics.All(),
		GoVersion:    version,
		Cardinality:  rmCardinality(),
	})
	if err != nil {
		log.Fatalf("executing template: %v", err)
	}

	// Format it.
	result, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("formatting code: %v", err)
	}

	// Write it to a file.
	fname := fmt.Sprintf("go_collector_metrics_%s_test.go", version.Abbr())
	if err := os.WriteFile(fname, result, 0o644); err != nil {
		log.Fatalf("writing file: %v", err)
	}
}

type goVersion int

func (g goVersion) String() string {
	return fmt.Sprintf("go1.%d", g)
}

func (g goVersion) Abbr() string {
	return fmt.Sprintf("go1%d", g)
}

func parseVersion(s string) (goVersion, error) {
	i := strings.IndexRune(s, '.')
	if i < 0 {
		return goVersion(-1), fmt.Errorf("bad Go version format")
	}
	i, err := strconv.Atoi(s[i+1:])
	return goVersion(i), err
}

func rmCardinality() int {
	cardinality := 0

	// Collect all histogram samples so that we can get their buckets.
	// The API guarantees that the buckets are always fixed for the lifetime
	// of the process.
	var histograms []metrics.Sample
	for _, d := range metrics.All() {
		if d.Kind == metrics.KindFloat64Histogram {
			histograms = append(histograms, metrics.Sample{Name: d.Name})
		} else {
			cardinality++
		}
	}

	// Handle histograms.
	metrics.Read(histograms)
	for i := range histograms {
		name := histograms[i].Name
		buckets := internal.RuntimeMetricsBucketsForUnit(
			histograms[i].Value.Float64Histogram().Buckets,
			name[strings.IndexRune(name, ':')+1:],
		)
		cardinality += len(buckets) + 3 // Plus total count, sum, and the implicit infinity bucket.
		// runtime/metrics bucket boundaries are lower-bound-inclusive, but
		// always represents each actual *boundary* so Buckets is always
		// 1 longer than Counts, while in Prometheus the mapping is one-to-one,
		// as the bottom bucket extends to -Inf, and the top infinity bucket is
		// implicit. Therefore, we should have one fewer bucket than is listed
		// above.
		cardinality--
		if buckets[len(buckets)-1] == math.Inf(1) {
			// We already counted the infinity bucket separately.
			cardinality--
		}
	}

	return cardinality
}

var testFile = template.Must(template.New("testFile").Funcs(map[string]interface{}{
	"rm2prom": func(d metrics.Description) string {
		ns, ss, n, ok := internal.RuntimeMetricsToProm(&d)
		if !ok {
			return ""
		}
		return prometheus.BuildFQName(ns, ss, n)
	},
	"nextVersion": func(version goVersion) string {
		return (version + goVersion(1)).String()
	},
}).Parse(`// Code generated by gen_go_collector_metrics_set.go; DO NOT EDIT.
//go:generate go run gen_go_collector_metrics_set.go {{.GoVersion}}

//go:build {{.GoVersion}} && !{{nextVersion .GoVersion}}
// +build {{.GoVersion}},!{{nextVersion .GoVersion}}

package prometheus

var expectedRuntimeMetrics = map[string]string{
{{- range .Descriptions -}}
	{{- $trans := rm2prom . -}}
	{{- if ne $trans "" }}
	{{.Name | printf "%q"}}: {{$trans | printf "%q"}},
	{{- end -}}
{{end}}
}

const expectedRuntimeMetricsCardinality = {{.Cardinality}}
`))
<commit_msg>Fix conflicts<commit_after>// Copyright 2021 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build ignore
// +build ignore

package main

import (
	"bytes"
	"fmt"
	"go/format"
	"log"
	"math"
	"os"
	"runtime"
	"runtime/metrics"
	"strconv"
	"strings"
	"text/template"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/internal"
)

func main() {
	if len(os.Args) != 2 {
		log.Fatal("requires Go version (e.g. go1.17) as an argument")
	}
	toolVersion := runtime.Version()
	if majorVersion := toolVersion[:strings.LastIndexByte(toolVersion, '.')]; majorVersion != os.Args[1] {
		log.Fatalf("using Go version %q but expected Go version %q", majorVersion, os.Args[1])
	}
	version, err := parseVersion(mv)
	if err != nil {
		log.Fatalf("parsing Go version: %v", err)
	}

	// Generate code.
	var buf bytes.Buffer
	err = testFile.Execute(&buf, struct {
		Descriptions []metrics.Description
		GoVersion    goVersion
		Cardinality  int
	}{
		Descriptions: metrics.All(),
		GoVersion:    version,
		Cardinality:  rmCardinality(),
	})
	if err != nil {
		log.Fatalf("executing template: %v", err)
	}

	// Format it.
	result, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("formatting code: %v", err)
	}

	// Write it to a file.
	fname := fmt.Sprintf("go_collector_metrics_%s_test.go", version.Abbr())
	if err := os.WriteFile(fname, result, 0o644); err != nil {
		log.Fatalf("writing file: %v", err)
	}
}

type goVersion int

func (g goVersion) String() string {
	return fmt.Sprintf("go1.%d", g)
}

func (g goVersion) Abbr() string {
	return fmt.Sprintf("go1%d", g)
}

func parseVersion(s string) (goVersion, error) {
	i := strings.IndexRune(s, '.')
	if i < 0 {
		return goVersion(-1), fmt.Errorf("bad Go version format")
	}
	i, err := strconv.Atoi(s[i+1:])
	return goVersion(i), err
}

func rmCardinality() int {
	cardinality := 0

	// Collect all histogram samples so that we can get their buckets.
	// The API guarantees that the buckets are always fixed for the lifetime
	// of the process.
	var histograms []metrics.Sample
	for _, d := range metrics.All() {
		if d.Kind == metrics.KindFloat64Histogram {
			histograms = append(histograms, metrics.Sample{Name: d.Name})
		} else {
			cardinality++
		}
	}

	// Handle histograms.
	metrics.Read(histograms)
	for i := range histograms {
		name := histograms[i].Name
		buckets := internal.RuntimeMetricsBucketsForUnit(
			histograms[i].Value.Float64Histogram().Buckets,
			name[strings.IndexRune(name, ':')+1:],
		)
		cardinality += len(buckets) + 3 // Plus total count, sum, and the implicit infinity bucket.
		// runtime/metrics bucket boundaries are lower-bound-inclusive, but
		// always represents each actual *boundary* so Buckets is always
		// 1 longer than Counts, while in Prometheus the mapping is one-to-one,
		// as the bottom bucket extends to -Inf, and the top infinity bucket is
		// implicit. Therefore, we should have one fewer bucket than is listed
		// above.
		cardinality--
		if buckets[len(buckets)-1] == math.Inf(1) {
			// We already counted the infinity bucket separately.
			cardinality--
		}
	}

	return cardinality
}

var testFile = template.Must(template.New("testFile").Funcs(map[string]interface{}{
	"rm2prom": func(d metrics.Description) string {
		ns, ss, n, ok := internal.RuntimeMetricsToProm(&d)
		if !ok {
			return ""
		}
		return prometheus.BuildFQName(ns, ss, n)
	},
	"nextVersion": func(version goVersion) string {
		return (version + goVersion(1)).String()
	},
}).Parse(`// Code generated by gen_go_collector_metrics_set.go; DO NOT EDIT.
//go:generate go run gen_go_collector_metrics_set.go {{.GoVersion}}

//go:build {{.GoVersion}} && !{{nextVersion .GoVersion}}
// +build {{.GoVersion}},!{{nextVersion .GoVersion}}

package prometheus

var expectedRuntimeMetrics = map[string]string{
{{- range .Descriptions -}}
	{{- $trans := rm2prom . -}}
	{{- if ne $trans "" }}
	{{.Name | printf "%q"}}: {{$trans | printf "%q"}},
	{{- end -}}
{{end}}
}

const expectedRuntimeMetricsCardinality = {{.Cardinality}}
`))
<|endoftext|>


<commit_before>package api

import (
	"cf"
	"cf/configuration"
	"cf/models"
	"cf/net"
	"fmt"
	neturl "net/url"
	"strings"
)

type PaginatedUserResources struct {
	NextUrl   string `json:"next_url"`
	Resources []UserResource
}

type UserResource struct {
	Resource
	Entity UserEntity
}

type UAAUserResources struct {
	Resources []struct {
		Id       string
		Username string
	}
}

func (resource UserResource) ToFields() models.UserFields {
	return models.UserFields{
		Guid:    resource.Metadata.Guid,
		IsAdmin: resource.Entity.Admin,
	}
}

type UserEntity struct {
	Entity
	Admin bool
}

var orgRoleToPathMap = map[string]string{
	models.ORG_USER:        "users",
	models.ORG_MANAGER:     "managers",
	models.BILLING_MANAGER: "billing_managers",
	models.ORG_AUDITOR:     "auditors",
}

var spaceRoleToPathMap = map[string]string{
	models.SPACE_MANAGER:   "managers",
	models.SPACE_DEVELOPER: "developers",
	models.SPACE_AUDITOR:   "auditors",
}

type UserRepository interface {
	FindByUsername(username string) (user models.UserFields, apiResponse net.ApiResponse)
	ListUsersInOrgForRole(orgGuid string, role string) ([]models.UserFields, net.ApiResponse)
	ListUsersInSpaceForRole(spaceGuid string, role string) ([]models.UserFields, net.ApiResponse)
	Create(username, password string) (apiResponse net.ApiResponse)
	Delete(userGuid string) (apiResponse net.ApiResponse)
	SetOrgRole(userGuid, orgGuid, role string) (apiResponse net.ApiResponse)
	UnsetOrgRole(userGuid, orgGuid, role string) (apiResponse net.ApiResponse)
	SetSpaceRole(userGuid, spaceGuid, orgGuid, role string) (apiResponse net.ApiResponse)
	UnsetSpaceRole(userGuid, spaceGuid, role string) (apiResponse net.ApiResponse)
}

type CloudControllerUserRepository struct {
	config       *configuration.Configuration
	uaaGateway   net.Gateway
	ccGateway    net.Gateway
	endpointRepo EndpointRepository
}

func NewCloudControllerUserRepository(config *configuration.Configuration, uaaGateway net.Gateway, ccGateway net.Gateway, endpointRepo EndpointRepository) (repo CloudControllerUserRepository) {
	repo.config = config
	repo.uaaGateway = uaaGateway
	repo.ccGateway = ccGateway
	repo.endpointRepo = endpointRepo
	return
}

func (repo CloudControllerUserRepository) FindByUsername(username string) (user models.UserFields, apiResponse net.ApiResponse) {
	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	usernameFilter := neturl.QueryEscape(fmt.Sprintf(`userName Eq "%s"`, username))
	path := fmt.Sprintf("%s/Users?attributes=id,userName&filter=%s", uaaEndpoint, usernameFilter)

	users, apiResponse := repo.updateOrFindUsersWithUAAPath([]models.UserFields{}, path)
	if len(users) == 0 {
		apiResponse = net.NewNotFoundApiResponse("UserFields %s not found", username)
		return
	}

	user = users[0]
	return
}

func (repo CloudControllerUserRepository) ListUsersInOrgForRole(orgGuid string, roleName string) (users []models.UserFields, apiResponse net.ApiResponse) {
	return repo.listUsersWithPath(fmt.Sprintf("/v2/organizations/%s/%s", orgGuid, orgRoleToPathMap[roleName]))
}

func (repo CloudControllerUserRepository) ListUsersInSpaceForRole(spaceGuid string, roleName string) (users []models.UserFields, apiResponse net.ApiResponse) {
	return repo.listUsersWithPath(fmt.Sprintf("/v2/spaces/%s/%s", spaceGuid, spaceRoleToPathMap[roleName]))
}

func (repo CloudControllerUserRepository) listUsersWithPath(path string) (users []models.UserFields, apiResponse net.ApiResponse) {
	guidFilters := []string{}

	apiResponse = repo.ccGateway.ListPaginatedResources(
		repo.config.Target,
		repo.config.AccessToken,
		path,
		UserResource{},
		func(resource interface{}) bool {
			user := resource.(UserResource).ToFields()
			users = append(users, user)
			guidFilters = append(guidFilters, fmt.Sprintf(`Id eq "%s"`, user.Guid))
			return true
		})
	if apiResponse.IsNotSuccessful() {
		return
	}

	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	filter := strings.Join(guidFilters, " or ")
	usersURL := fmt.Sprintf("%s/Users?attributes=id,userName&filter=%s", uaaEndpoint, neturl.QueryEscape(filter))
	users, apiResponse = repo.updateOrFindUsersWithUAAPath(users, usersURL)
	return
}

func (repo CloudControllerUserRepository) updateOrFindUsersWithUAAPath(ccUsers []models.UserFields, path string) (updatedUsers []models.UserFields, apiResponse net.ApiResponse) {
	uaaResponse := new(UAAUserResources)
	apiResponse = repo.uaaGateway.GetResource(path, repo.config.AccessToken, uaaResponse)
	if apiResponse.IsNotSuccessful() {
		return
	}

	for _, uaaResource := range uaaResponse.Resources {
		var ccUserFields models.UserFields

		for _, u := range ccUsers {
			if u.Guid == uaaResource.Id {
				ccUserFields = u
				break
			}
		}

		updatedUsers = append(updatedUsers, models.UserFields{
			Guid:     uaaResource.Id,
			Username: uaaResource.Username,
			IsAdmin:  ccUserFields.IsAdmin,
		})
	}
	return
}

func (repo CloudControllerUserRepository) Create(username, password string) (apiResponse net.ApiResponse) {
	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	path := fmt.Sprintf("%s/Users", uaaEndpoint)
	body := fmt.Sprintf(`{
  "userName": "%s",
  "emails": [{"value":"%s"}],
  "password": "%s",
  "name": {"givenName":"%s", "familyName":"%s"}
}`,
		username,
		username,
		password,
		username,
		username,
	)
	request, apiResponse := repo.uaaGateway.NewRequest("POST", path, repo.config.AccessToken, strings.NewReader(body))
	if apiResponse.IsNotSuccessful() {
		return
	}

	type uaaUserFields struct {
		Id string
	}
	createUserResponse := &uaaUserFields{}

	_, apiResponse = repo.uaaGateway.PerformRequestForJSONResponse(request, createUserResponse)
	if apiResponse.IsNotSuccessful() {
		return
	}

	path = fmt.Sprintf("%s/v2/users", repo.config.Target)
	body = fmt.Sprintf(`{"guid":"%s"}`, createUserResponse.Id)
	return repo.ccGateway.CreateResource(path, repo.config.AccessToken, strings.NewReader(body))
}

func (repo CloudControllerUserRepository) Delete(userGuid string) (apiResponse net.ApiResponse) {
	path := fmt.Sprintf("%s/v2/users/%s", repo.config.Target, userGuid)

	apiResponse = repo.ccGateway.DeleteResource(path, repo.config.AccessToken)
	if apiResponse.IsNotSuccessful() && apiResponse.ErrorCode != cf.USER_NOT_FOUND {
		return
	}

	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	path = fmt.Sprintf("%s/Users/%s", uaaEndpoint, userGuid)
	return repo.uaaGateway.DeleteResource(path, repo.config.AccessToken)
}

func (repo CloudControllerUserRepository) SetOrgRole(userGuid string, orgGuid string, role string) (apiResponse net.ApiResponse) {
	apiResponse = repo.setOrUnsetOrgRole("PUT", userGuid, orgGuid, role)
	if apiResponse.IsNotSuccessful() {
		return
	}
	return repo.addOrgUserRole(userGuid, orgGuid)
}

func (repo CloudControllerUserRepository) UnsetOrgRole(userGuid, orgGuid, role string) (apiResponse net.ApiResponse) {
	return repo.setOrUnsetOrgRole("DELETE", userGuid, orgGuid, role)
}

func (repo CloudControllerUserRepository) setOrUnsetOrgRole(verb, userGuid, orgGuid, role string) (apiResponse net.ApiResponse) {
	rolePath, found := orgRoleToPathMap[role]

	if !found {
		apiResponse = net.NewApiResponseWithMessage("Invalid Role %s", role)
		return
	}

	path := fmt.Sprintf("%s/v2/organizations/%s/%s/%s", repo.config.Target, orgGuid, rolePath, userGuid)

	request, apiResponse := repo.ccGateway.NewRequest(verb, path, repo.config.AccessToken, nil)
	if apiResponse.IsNotSuccessful() {
		return
	}

	apiResponse = repo.ccGateway.PerformRequest(request)
	if apiResponse.IsNotSuccessful() {
		return
	}
	return
}

func (repo CloudControllerUserRepository) SetSpaceRole(userGuid, spaceGuid, orgGuid, role string) (apiResponse net.ApiResponse) {
	rolePath, apiResponse := repo.checkSpaceRole(userGuid, spaceGuid, role)
	if apiResponse.IsNotSuccessful() {
		return
	}

	apiResponse = repo.addOrgUserRole(userGuid, orgGuid)
	if apiResponse.IsNotSuccessful() {
		return
	}

	return repo.ccGateway.UpdateResource(rolePath, repo.config.AccessToken, nil)
}

func (repo CloudControllerUserRepository) UnsetSpaceRole(userGuid, spaceGuid, role string) (apiResponse net.ApiResponse) {
	rolePath, apiResponse := repo.checkSpaceRole(userGuid, spaceGuid, role)
	if apiResponse.IsNotSuccessful() {
		return
	}
	return repo.ccGateway.DeleteResource(rolePath, repo.config.AccessToken)
}

func (repo CloudControllerUserRepository) checkSpaceRole(userGuid, spaceGuid, role string) (fullPath string, apiResponse net.ApiResponse) {
	rolePath, found := spaceRoleToPathMap[role]

	if !found {
		apiResponse = net.NewApiResponseWithMessage("Invalid Role %s", role)
	}

	fullPath = fmt.Sprintf("%s/v2/spaces/%s/%s/%s", repo.config.Target, spaceGuid, rolePath, userGuid)
	return
}

func (repo CloudControllerUserRepository) addOrgUserRole(userGuid, orgGuid string) (apiResponse net.ApiResponse) {
	path := fmt.Sprintf("%s/v2/organizations/%s/users/%s", repo.config.Target, orgGuid, userGuid)
	return repo.ccGateway.UpdateResource(path, repo.config.AccessToken, nil)
}
<commit_msg>Remove unused struct in Users Repo<commit_after>package api

import (
	"cf"
	"cf/configuration"
	"cf/models"
	"cf/net"
	"fmt"
	neturl "net/url"
	"strings"
)

type UserResource struct {
	Resource
	Entity UserEntity
}

type UAAUserResources struct {
	Resources []struct {
		Id       string
		Username string
	}
}

func (resource UserResource) ToFields() models.UserFields {
	return models.UserFields{
		Guid:    resource.Metadata.Guid,
		IsAdmin: resource.Entity.Admin,
	}
}

type UserEntity struct {
	Entity
	Admin bool
}

var orgRoleToPathMap = map[string]string{
	models.ORG_USER:        "users",
	models.ORG_MANAGER:     "managers",
	models.BILLING_MANAGER: "billing_managers",
	models.ORG_AUDITOR:     "auditors",
}

var spaceRoleToPathMap = map[string]string{
	models.SPACE_MANAGER:   "managers",
	models.SPACE_DEVELOPER: "developers",
	models.SPACE_AUDITOR:   "auditors",
}

type UserRepository interface {
	FindByUsername(username string) (user models.UserFields, apiResponse net.ApiResponse)
	ListUsersInOrgForRole(orgGuid string, role string) ([]models.UserFields, net.ApiResponse)
	ListUsersInSpaceForRole(spaceGuid string, role string) ([]models.UserFields, net.ApiResponse)
	Create(username, password string) (apiResponse net.ApiResponse)
	Delete(userGuid string) (apiResponse net.ApiResponse)
	SetOrgRole(userGuid, orgGuid, role string) (apiResponse net.ApiResponse)
	UnsetOrgRole(userGuid, orgGuid, role string) (apiResponse net.ApiResponse)
	SetSpaceRole(userGuid, spaceGuid, orgGuid, role string) (apiResponse net.ApiResponse)
	UnsetSpaceRole(userGuid, spaceGuid, role string) (apiResponse net.ApiResponse)
}

type CloudControllerUserRepository struct {
	config       *configuration.Configuration
	uaaGateway   net.Gateway
	ccGateway    net.Gateway
	endpointRepo EndpointRepository
}

func NewCloudControllerUserRepository(config *configuration.Configuration, uaaGateway net.Gateway, ccGateway net.Gateway, endpointRepo EndpointRepository) (repo CloudControllerUserRepository) {
	repo.config = config
	repo.uaaGateway = uaaGateway
	repo.ccGateway = ccGateway
	repo.endpointRepo = endpointRepo
	return
}

func (repo CloudControllerUserRepository) FindByUsername(username string) (user models.UserFields, apiResponse net.ApiResponse) {
	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	usernameFilter := neturl.QueryEscape(fmt.Sprintf(`userName Eq "%s"`, username))
	path := fmt.Sprintf("%s/Users?attributes=id,userName&filter=%s", uaaEndpoint, usernameFilter)

	users, apiResponse := repo.updateOrFindUsersWithUAAPath([]models.UserFields{}, path)
	if len(users) == 0 {
		apiResponse = net.NewNotFoundApiResponse("UserFields %s not found", username)
		return
	}

	user = users[0]
	return
}

func (repo CloudControllerUserRepository) ListUsersInOrgForRole(orgGuid string, roleName string) (users []models.UserFields, apiResponse net.ApiResponse) {
	return repo.listUsersWithPath(fmt.Sprintf("/v2/organizations/%s/%s", orgGuid, orgRoleToPathMap[roleName]))
}

func (repo CloudControllerUserRepository) ListUsersInSpaceForRole(spaceGuid string, roleName string) (users []models.UserFields, apiResponse net.ApiResponse) {
	return repo.listUsersWithPath(fmt.Sprintf("/v2/spaces/%s/%s", spaceGuid, spaceRoleToPathMap[roleName]))
}

func (repo CloudControllerUserRepository) listUsersWithPath(path string) (users []models.UserFields, apiResponse net.ApiResponse) {
	guidFilters := []string{}

	apiResponse = repo.ccGateway.ListPaginatedResources(
		repo.config.Target,
		repo.config.AccessToken,
		path,
		UserResource{},
		func(resource interface{}) bool {
			user := resource.(UserResource).ToFields()
			users = append(users, user)
			guidFilters = append(guidFilters, fmt.Sprintf(`Id eq "%s"`, user.Guid))
			return true
		})
	if apiResponse.IsNotSuccessful() {
		return
	}

	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	filter := strings.Join(guidFilters, " or ")
	usersURL := fmt.Sprintf("%s/Users?attributes=id,userName&filter=%s", uaaEndpoint, neturl.QueryEscape(filter))
	users, apiResponse = repo.updateOrFindUsersWithUAAPath(users, usersURL)
	return
}

func (repo CloudControllerUserRepository) updateOrFindUsersWithUAAPath(ccUsers []models.UserFields, path string) (updatedUsers []models.UserFields, apiResponse net.ApiResponse) {
	uaaResponse := new(UAAUserResources)
	apiResponse = repo.uaaGateway.GetResource(path, repo.config.AccessToken, uaaResponse)
	if apiResponse.IsNotSuccessful() {
		return
	}

	for _, uaaResource := range uaaResponse.Resources {
		var ccUserFields models.UserFields

		for _, u := range ccUsers {
			if u.Guid == uaaResource.Id {
				ccUserFields = u
				break
			}
		}

		updatedUsers = append(updatedUsers, models.UserFields{
			Guid:     uaaResource.Id,
			Username: uaaResource.Username,
			IsAdmin:  ccUserFields.IsAdmin,
		})
	}
	return
}

func (repo CloudControllerUserRepository) Create(username, password string) (apiResponse net.ApiResponse) {
	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	path := fmt.Sprintf("%s/Users", uaaEndpoint)
	body := fmt.Sprintf(`{
  "userName": "%s",
  "emails": [{"value":"%s"}],
  "password": "%s",
  "name": {"givenName":"%s", "familyName":"%s"}
}`,
		username,
		username,
		password,
		username,
		username,
	)
	request, apiResponse := repo.uaaGateway.NewRequest("POST", path, repo.config.AccessToken, strings.NewReader(body))
	if apiResponse.IsNotSuccessful() {
		return
	}

	type uaaUserFields struct {
		Id string
	}
	createUserResponse := &uaaUserFields{}

	_, apiResponse = repo.uaaGateway.PerformRequestForJSONResponse(request, createUserResponse)
	if apiResponse.IsNotSuccessful() {
		return
	}

	path = fmt.Sprintf("%s/v2/users", repo.config.Target)
	body = fmt.Sprintf(`{"guid":"%s"}`, createUserResponse.Id)
	return repo.ccGateway.CreateResource(path, repo.config.AccessToken, strings.NewReader(body))
}

func (repo CloudControllerUserRepository) Delete(userGuid string) (apiResponse net.ApiResponse) {
	path := fmt.Sprintf("%s/v2/users/%s", repo.config.Target, userGuid)

	apiResponse = repo.ccGateway.DeleteResource(path, repo.config.AccessToken)
	if apiResponse.IsNotSuccessful() && apiResponse.ErrorCode != cf.USER_NOT_FOUND {
		return
	}

	uaaEndpoint, apiResponse := repo.endpointRepo.GetUAAEndpoint()
	if apiResponse.IsNotSuccessful() {
		return
	}

	path = fmt.Sprintf("%s/Users/%s", uaaEndpoint, userGuid)
	return repo.uaaGateway.DeleteResource(path, repo.config.AccessToken)
}

func (repo CloudControllerUserRepository) SetOrgRole(userGuid string, orgGuid string, role string) (apiResponse net.ApiResponse) {
	apiResponse = repo.setOrUnsetOrgRole("PUT", userGuid, orgGuid, role)
	if apiResponse.IsNotSuccessful() {
		return
	}
	return repo.addOrgUserRole(userGuid, orgGuid)
}

func (repo CloudControllerUserRepository) UnsetOrgRole(userGuid, orgGuid, role string) (apiResponse net.ApiResponse) {
	return repo.setOrUnsetOrgRole("DELETE", userGuid, orgGuid, role)
}

func (repo CloudControllerUserRepository) setOrUnsetOrgRole(verb, userGuid, orgGuid, role string) (apiResponse net.ApiResponse) {
	rolePath, found := orgRoleToPathMap[role]

	if !found {
		apiResponse = net.NewApiResponseWithMessage("Invalid Role %s", role)
		return
	}

	path := fmt.Sprintf("%s/v2/organizations/%s/%s/%s", repo.config.Target, orgGuid, rolePath, userGuid)

	request, apiResponse := repo.ccGateway.NewRequest(verb, path, repo.config.AccessToken, nil)
	if apiResponse.IsNotSuccessful() {
		return
	}

	apiResponse = repo.ccGateway.PerformRequest(request)
	if apiResponse.IsNotSuccessful() {
		return
	}
	return
}

func (repo CloudControllerUserRepository) SetSpaceRole(userGuid, spaceGuid, orgGuid, role string) (apiResponse net.ApiResponse) {
	rolePath, apiResponse := repo.checkSpaceRole(userGuid, spaceGuid, role)
	if apiResponse.IsNotSuccessful() {
		return
	}

	apiResponse = repo.addOrgUserRole(userGuid, orgGuid)
	if apiResponse.IsNotSuccessful() {
		return
	}

	return repo.ccGateway.UpdateResource(rolePath, repo.config.AccessToken, nil)
}

func (repo CloudControllerUserRepository) UnsetSpaceRole(userGuid, spaceGuid, role string) (apiResponse net.ApiResponse) {
	rolePath, apiResponse := repo.checkSpaceRole(userGuid, spaceGuid, role)
	if apiResponse.IsNotSuccessful() {
		return
	}
	return repo.ccGateway.DeleteResource(rolePath, repo.config.AccessToken)
}

func (repo CloudControllerUserRepository) checkSpaceRole(userGuid, spaceGuid, role string) (fullPath string, apiResponse net.ApiResponse) {
	rolePath, found := spaceRoleToPathMap[role]

	if !found {
		apiResponse = net.NewApiResponseWithMessage("Invalid Role %s", role)
	}

	fullPath = fmt.Sprintf("%s/v2/spaces/%s/%s/%s", repo.config.Target, spaceGuid, rolePath, userGuid)
	return
}

func (repo CloudControllerUserRepository) addOrgUserRole(userGuid, orgGuid string) (apiResponse net.ApiResponse) {
	path := fmt.Sprintf("%s/v2/organizations/%s/users/%s", repo.config.Target, orgGuid, userGuid)
	return repo.ccGateway.UpdateResource(path, repo.config.AccessToken, nil)
}
<|endoftext|>


<commit_before>package poslog

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

// ImportXML takes a POSLog XML file as the argument and returns
// a POSLog
func ImportXML(filename string) (p POSLog) {
	byteXML, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("FUCK", err)
	}

	xml.Unmarshal(byteXML, &p)
	p.appendFilename(filepath.Base(filename))
	// p.appendDayID()

	return
}

func importReaderXML(f io.Reader, filename string) (p POSLog) {
	byteXML, err := ioutil.ReadAll(f)
	if err != nil {
		fmt.Println("FUCK", err)
	}
	xml.Unmarshal(byteXML, &p)
	p.appendFilename(filepath.Base(filename))
	// p.appendDayID()
	return
}

// WriteXML writes a POSLog object to an XML file given as first argument
func (p *POSLog) WriteXML(filename string) {
	// Drop any other extension and stick a xml on there
	filename = strings.TrimSuffix(filename, filepath.Ext(filename))
	filename = filename + ".xml"

	ioutil.WriteFile(filename, createXML(*p), 0666)
}

func createXML(p POSLog) []byte {
	xs, err := xml.MarshalIndent(p, "", "    ")
	if err != nil {
		fmt.Println(err)
	}
	return xs
}

// WriteJSON writes a POSLog object to an json file given as first argument
func WriteJSON(filename string, p POSLog) {
	// Drop any other extension and stick a json on there
	filename = strings.TrimSuffix(filename, filepath.Ext(filename))
	filename = filename + ".json"
	ioutil.WriteFile(filename, createJSON(p), 0666)
}

func createJSON(p POSLog) []byte {
	js, err := json.MarshalIndent(p, "", "  ")
	if err != nil {
		fmt.Println(err)
	}
	return js
}

func WriteJSONs(folder string, ps POSLogs) {
	os.Mkdir(folder, 0777)
	for _, p := range ps.POSLogs {
		op := filepath.Join(folder, p.Filename)
		WriteJSON(op, p)
	}
}

func (tr *Transaction) Json() string {
	js, err := json.Marshal(tr)
	if err != nil {
		log.Println(err)
	}
	return string(js)
}
<commit_msg>remove transaction to json<commit_after>package poslog

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

// ImportXML takes a POSLog XML file as the argument and returns
// a POSLog
func ImportXML(filename string) (p POSLog) {
	byteXML, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("FUCK", err)
	}

	xml.Unmarshal(byteXML, &p)
	p.appendFilename(filepath.Base(filename))
	// p.appendDayID()

	return
}

func importReaderXML(f io.Reader, filename string) (p POSLog) {
	byteXML, err := ioutil.ReadAll(f)
	if err != nil {
		fmt.Println("FUCK", err)
	}
	xml.Unmarshal(byteXML, &p)
	p.appendFilename(filepath.Base(filename))
	// p.appendDayID()
	return
}

// WriteXML writes a POSLog object to an XML file given as first argument
func (p *POSLog) WriteXML(filename string) {
	// Drop any other extension and stick a xml on there
	filename = strings.TrimSuffix(filename, filepath.Ext(filename))
	filename = filename + ".xml"

	ioutil.WriteFile(filename, createXML(*p), 0666)
}

func createXML(p POSLog) []byte {
	xs, err := xml.MarshalIndent(p, "", "    ")
	if err != nil {
		fmt.Println(err)
	}
	return xs
}

// WriteJSON writes a POSLog object to an json file given as first argument
func WriteJSON(filename string, p POSLog) {
	// Drop any other extension and stick a json on there
	filename = strings.TrimSuffix(filename, filepath.Ext(filename))
	filename = filename + ".json"
	ioutil.WriteFile(filename, createJSON(p), 0666)
}

func createJSON(p POSLog) []byte {
	js, err := json.MarshalIndent(p, "", "  ")
	if err != nil {
		fmt.Println(err)
	}
	return js
}

// WriteJSONs writes multiple POSLOGS (in a postlogs object) to multiple json files in a folder
func WriteJSONs(folder string, ps POSLogs) {
	os.Mkdir(folder, 0777)
	for _, p := range ps.POSLogs {
		op := filepath.Join(folder, p.Filename)
		WriteJSON(op, p)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"strings"
)

// file holds a full file path, processed data, and error (if any).
type file struct {
	path string
	data string
	err  error
}

// filesInfo holds a slice of os.FileInfo along with the directory the
// contents came from.
type filesInfo struct {
	dir string
	fsi []os.FileInfo
}

// gatherFilesInfo is a convenience function which grabs all gzipped files
// within the provided directory with a depth of 1.
func gatherFilesInfo(dir string) (*filesInfo, error) {
	fsi, err := ioutil.ReadDir(dir)
	if err != nil {
		return nil, err
	}

	for k := len(fsi) - 1; k >= 0; k-- {
		// remove directories, and files without the correct extension
		if fsi[k].IsDir() || !strings.HasSuffix(fsi[k].Name(), ".gz") {
			fsi = append(fsi[:k], fsi[k+1:]...)
		}
	}

	return &filesInfo{dir: dir, fsi: fsi}, nil
}
<commit_msg>Remove reference to full path in file comment.<commit_after>package main

import (
	"io/ioutil"
	"os"
	"strings"
)

// file holds a file path (with dir), processed data, and error (if any).
type file struct {
	path string
	data string
	err  error
}

// filesInfo holds a slice of os.FileInfo along with the directory the
// contents came from.
type filesInfo struct {
	dir string
	fsi []os.FileInfo
}

// gatherFilesInfo is a convenience function which grabs all gzipped files
// within the provided directory with a depth of 1.
func gatherFilesInfo(dir string) (*filesInfo, error) {
	fsi, err := ioutil.ReadDir(dir)
	if err != nil {
		return nil, err
	}

	for k := len(fsi) - 1; k >= 0; k-- {
		// remove directories, and files without the correct extension
		if fsi[k].IsDir() || !strings.HasSuffix(fsi[k].Name(), ".gz") {
			fsi = append(fsi[:k], fsi[k+1:]...)
		}
	}

	return &filesInfo{dir: dir, fsi: fsi}, nil
}
<|endoftext|>


<commit_before>package dir

import (
	"bytes"
	"os"
	"strings"
	"time"
)

type fileBytes struct {
	data    []byte
	modTime time.Time
}

func FileBytes(data []byte, modTime time.Time) Node {
	return fileBytes{
		data,
		modTime,
	}
}

func (f fileBytes) Size() int64 {
	return int64(len(f.data))
}

func (fileBytes) Mode() os.FileMode {
	return ModeFile
}

func (f fileBytes) ModTime() time.Time {
	return f.modTime
}

func (f fileBytes) Open() (File, error) {
	return fileBytesOpen{bytes.NewReader(f.data)}
}

type fileBytesOpen struct {
	*bytes.Reader
}

func (fileBytesOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

func (fileBytesOpen) Close() error {
	return nil
}

type fileString struct {
	data    string
	modTime time.Time
}

func FileString(data string, modTime time.Time) Node {
	return fileString{
		data,
		modTime,
	}
}

func (f fileString) Size() int64 {
	return int64(len(f.data))
}

func (fileString) Mode() os.FileMode {
	return ModeFile
}

func (f fileString) ModTime() time.Time {
	return f.modTime
}

func (f fileString) Open() (File, error) {
	return fileStringOpen{strings.NewReader(f.data)}
}

type fileStringOpen struct {
	*strings.Reader
}

func (fileStringOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

func (fileStringOpen) Close() error {
	return nil
}

type OSFile string

func (o OSFile) Size() int64 {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Size()
}

func (o OSFile) Mode() os.FileMode {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Mode()
}

func (o OSFile) ModTime() time.Time {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.ModTime()
}

func (o OSFile) Open() (File, error) {
	return os.Open(string(o))
}
<commit_msg>Incorrect package name<commit_after>package httpdir

import (
	"bytes"
	"os"
	"strings"
	"time"
)

type fileBytes struct {
	data    []byte
	modTime time.Time
}

func FileBytes(data []byte, modTime time.Time) Node {
	return fileBytes{
		data,
		modTime,
	}
}

func (f fileBytes) Size() int64 {
	return int64(len(f.data))
}

func (fileBytes) Mode() os.FileMode {
	return ModeFile
}

func (f fileBytes) ModTime() time.Time {
	return f.modTime
}

func (f fileBytes) Open() (File, error) {
	return fileBytesOpen{bytes.NewReader(f.data)}
}

type fileBytesOpen struct {
	*bytes.Reader
}

func (fileBytesOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

func (fileBytesOpen) Close() error {
	return nil
}

type fileString struct {
	data    string
	modTime time.Time
}

func FileString(data string, modTime time.Time) Node {
	return fileString{
		data,
		modTime,
	}
}

func (f fileString) Size() int64 {
	return int64(len(f.data))
}

func (fileString) Mode() os.FileMode {
	return ModeFile
}

func (f fileString) ModTime() time.Time {
	return f.modTime
}

func (f fileString) Open() (File, error) {
	return fileStringOpen{strings.NewReader(f.data)}
}

type fileStringOpen struct {
	*strings.Reader
}

func (fileStringOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

func (fileStringOpen) Close() error {
	return nil
}

type OSFile string

func (o OSFile) Size() int64 {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Size()
}

func (o OSFile) Mode() os.FileMode {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Mode()
}

func (o OSFile) ModTime() time.Time {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.ModTime()
}

func (o OSFile) Open() (File, error) {
	return os.Open(string(o))
}
<|endoftext|>


<commit_before>package goScp

import (
	"os"
	"strings"
	"log"
)

func createNewFile(filename string) *os.File {
	file, err := os.Create(strings.TrimSpace(filename))
	if err != nil {
		log.Fatal(err)
	}

	return file
}

func writeParitalToFile(file *os.File, content []byte) {
	_, err := file.Write(content)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>gofmt<commit_after>package goScp

import (
	"log"
	"os"
	"strings"
)

func createNewFile(filename string) *os.File {
	file, err := os.Create(strings.TrimSpace(filename))
	if err != nil {
		log.Fatal(err)
	}

	return file
}

func writeParitalToFile(file *os.File, content []byte) {
	_, err := file.Write(content)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package srcgraph

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

	"github.com/sourcegraph/go-vcs/vcs"
	"sourcegraph.com/sourcegraph/srcgraph/buildstore"
	"sourcegraph.com/sourcegraph/srcgraph/config"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/scan"
	"sourcegraph.com/sourcegraph/srcgraph/task2"
)

type RepoContext struct {
	RepoRootDir string // Root directory containing repository being analyzed
	VCSType     string // VCS type (git or hg)
	CommitID    string // CommitID of current working directory
}

func NewRepoContext(targetDir string) (*RepoContext, error) {
	if !isDir(targetDir) {
		return nil, fmt.Errorf("directory not exist: %q", targetDir)
	}

	// VCS and root directory
	rc := new(RepoContext)
	for _, vcsType := range []string{"git", "hg"} {
		if d, err := getRepoRootDir(vcsType, targetDir); err == nil {
			rc.VCSType = vcsType
			rc.RepoRootDir = d
			break
		}
	}
	if rc.RepoRootDir == "" {
		return nil, fmt.Errorf("warning: failed to detect repository root dir for %q", targetDir)
	}

	// Determine current working tree commit ID.
	repo, err := vcs.Open(rc.VCSType, rc.RepoRootDir)
	if err != nil {
		return nil, err
	}
	var currentRevSpec string
	switch rc.VCSType {
	case "git":
		currentRevSpec = "HEAD"
	case "hg":
		currentRevSpec = "tip"
	}
	currentCommitID, err := repo.ResolveRevision(currentRevSpec)
	if err != nil {
		return nil, err
	}

	rc.CommitID = string(currentCommitID)

	// updateVCSIgnore("." + rc.VCS.ShortName() + "ignore") // TODO: desirable?
	return rc, nil
}

type JobContext struct {
	*RepoContext
	Repo *config.Repository // Repository-level configuration, read from .sourcegraph-data/config.json
}

func NewJobContext(targetDir string, x *task2.Context) (*JobContext, error) {
	rc, err := NewRepoContext(targetDir)
	if err != nil {
		return nil, err
	}
	jc := new(JobContext)
	jc.RepoContext = rc

	// get default URI (if URI is not specified in .sourcegraph file)
	// TODO(bliu): this seems like it should get pushed into ReadOrComputeRepositoryConfig...
	cloneURL, err := getVCSCloneURL(rc.VCSType, targetDir)
	if err != nil {
		return nil, err
	}
	uri := repo.MakeURI(cloneURL)

	jc.Repo, err = ReadOrComputeRepositoryConfig(jc.RepoRootDir, jc.CommitID, uri, x)
	if err != nil {
		return nil, err
	}

	return jc, nil
}

func ReadOrComputeRepositoryConfig(repoDir string, commitID string, repoURI repo.URI, x *task2.Context) (*config.Repository, error) {
	configFile, err := getConfigFile(repoDir, commitID)
	if err != nil {
		return nil, err
	}
	if isFile(configFile) {
		// Read
		f, err := os.Open(configFile)
		if err != nil {
			return nil, err
		}
		var c config.Repository
		err = json.NewDecoder(f).Decode(&c)
		if err != nil {
			return nil, err
		}
		return &c, nil
	} else {
		// Compute
		return scan.ReadDirConfigAndScan(repoDir, repoURI, x)
	}
}

func WriteRepositoryConfig(repoDir string, commitID string, c *config.Repository, overwrite bool) error {
	configFile, err := getConfigFile(repoDir, commitID)
	if err != nil {
		return err
	}
	if isFile(configFile) && !overwrite {
		return nil
	}

	err = os.MkdirAll(filepath.Dir(configFile), 0700)
	if err != nil {
		return err
	}
	b, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return err
	}

	return ioutil.WriteFile(configFile, b, 0700)
}

func getConfigFile(repoDir, commitID string) (string, error) {
	if repoDir == "" {
		return "", fmt.Errorf("no repository root directory")
	}
	repoStore, err := buildstore.NewRepositoryStore(repoDir)
	if err != nil {
		return "", err
	}
	rootDataDir, err := buildstore.RootDir(repoStore)
	if err != nil {
		return "", err
	}
	return filepath.Join(rootDataDir, repoStore.CommitPath(commitID), buildstore.CachedRepositoryConfigFilename), nil
}

func getRepoRootDir(vcsType string, dir string) (string, error) {
	var cmd *exec.Cmd
	switch vcsType {
	case "git":
		cmd = exec.Command("git", "rev-parse", "--show-toplevel")
	case "hg":
		cmd = exec.Command("hg", "root")
	}
	if cmd == nil {
		return "", fmt.Errorf("unrecognized VCS %v", vcsType)
	}
	cmd.Dir = dir
	out, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(out)), nil
}

func getVCSCloneURL(vcsType string, repoDir string) (string, error) {
	var cmd *exec.Cmd
	switch vcsType {
	case "git":
		cmd = exec.Command("git", "config", "remote.origin.url")
	case "hg":
		cmd = exec.Command("hg", "paths", "default")
	}
	if cmd == nil {
		return "", fmt.Errorf("unrecognized VCS %v", vcsType)
	}
	cmd.Dir = repoDir
	out, err := cmd.Output()
	if err != nil {
		return "", err
	}

	cloneURL := strings.TrimSpace(string(out))
	if vcsType == "git" {
		cloneURL = strings.Replace(cloneURL, "git@github.com:", "git://github.com/", 1)
	}
	return cloneURL, nil
}
<commit_msg>better error msg<commit_after>package srcgraph

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

	"github.com/sourcegraph/go-vcs/vcs"
	"sourcegraph.com/sourcegraph/srcgraph/buildstore"
	"sourcegraph.com/sourcegraph/srcgraph/config"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/scan"
	"sourcegraph.com/sourcegraph/srcgraph/task2"
)

type RepoContext struct {
	RepoRootDir string // Root directory containing repository being analyzed
	VCSType     string // VCS type (git or hg)
	CommitID    string // CommitID of current working directory
}

func NewRepoContext(targetDir string) (*RepoContext, error) {
	if !isDir(targetDir) {
		return nil, fmt.Errorf("directory not exist: %q", targetDir)
	}

	// VCS and root directory
	rc := new(RepoContext)
	for _, vcsType := range []string{"git", "hg"} {
		if d, err := getRepoRootDir(vcsType, targetDir); err == nil {
			rc.VCSType = vcsType
			rc.RepoRootDir = d
			break
		}
	}
	if rc.RepoRootDir == "" {
		return nil, fmt.Errorf("warning: failed to detect repository root dir for %q", targetDir)
	}

	// Determine current working tree commit ID.
	repo, err := vcs.Open(rc.VCSType, rc.RepoRootDir)
	if err != nil {
		return nil, err
	}
	var currentRevSpec string
	switch rc.VCSType {
	case "git":
		currentRevSpec = "HEAD"
	case "hg":
		currentRevSpec = "tip"
	}
	currentCommitID, err := repo.ResolveRevision(currentRevSpec)
	if err != nil {
		return nil, err
	}

	rc.CommitID = string(currentCommitID)

	// updateVCSIgnore("." + rc.VCS.ShortName() + "ignore") // TODO: desirable?
	return rc, nil
}

type JobContext struct {
	*RepoContext
	Repo *config.Repository // Repository-level configuration, read from .sourcegraph-data/config.json
}

func NewJobContext(targetDir string, x *task2.Context) (*JobContext, error) {
	rc, err := NewRepoContext(targetDir)
	if err != nil {
		return nil, err
	}
	jc := new(JobContext)
	jc.RepoContext = rc

	// get default URI (if URI is not specified in .sourcegraph file)
	// TODO(bliu): this seems like it should get pushed into ReadOrComputeRepositoryConfig...
	cloneURL, err := getVCSCloneURL(rc.VCSType, targetDir)
	if err != nil {
		return nil, err
	}
	uri := repo.MakeURI(cloneURL)

	jc.Repo, err = ReadOrComputeRepositoryConfig(jc.RepoRootDir, jc.CommitID, uri, x)
	if err != nil {
		return nil, err
	}

	return jc, nil
}

func ReadOrComputeRepositoryConfig(repoDir string, commitID string, repoURI repo.URI, x *task2.Context) (*config.Repository, error) {
	configFile, err := getConfigFile(repoDir, commitID)
	if err != nil {
		return nil, err
	}
	if isFile(configFile) {
		// Read
		f, err := os.Open(configFile)
		if err != nil {
			return nil, err
		}
		var c config.Repository
		err = json.NewDecoder(f).Decode(&c)
		if err != nil {
			return nil, err
		}
		return &c, nil
	} else {
		// Compute
		return scan.ReadDirConfigAndScan(repoDir, repoURI, x)
	}
}

func WriteRepositoryConfig(repoDir string, commitID string, c *config.Repository, overwrite bool) error {
	configFile, err := getConfigFile(repoDir, commitID)
	if err != nil {
		return err
	}
	if isFile(configFile) && !overwrite {
		return nil
	}

	err = os.MkdirAll(filepath.Dir(configFile), 0700)
	if err != nil {
		return err
	}
	b, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return err
	}

	return ioutil.WriteFile(configFile, b, 0700)
}

func getConfigFile(repoDir, commitID string) (string, error) {
	if repoDir == "" {
		return "", fmt.Errorf("no repository root directory")
	}
	repoStore, err := buildstore.NewRepositoryStore(repoDir)
	if err != nil {
		return "", err
	}
	rootDataDir, err := buildstore.RootDir(repoStore)
	if err != nil {
		return "", err
	}
	return filepath.Join(rootDataDir, repoStore.CommitPath(commitID), buildstore.CachedRepositoryConfigFilename), nil
}

func getRepoRootDir(vcsType string, dir string) (string, error) {
	var cmd *exec.Cmd
	switch vcsType {
	case "git":
		cmd = exec.Command("git", "rev-parse", "--show-toplevel")
	case "hg":
		cmd = exec.Command("hg", "root")
	}
	if cmd == nil {
		return "", fmt.Errorf("unrecognized VCS %v", vcsType)
	}
	cmd.Dir = dir
	out, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(out)), nil
}

func getVCSCloneURL(vcsType string, repoDir string) (string, error) {
	var cmd *exec.Cmd
	switch vcsType {
	case "git":
		cmd = exec.Command("git", "config", "remote.origin.url")
	case "hg":
		cmd = exec.Command("hg", "paths", "default")
	}
	if cmd == nil {
		return "", fmt.Errorf("unrecognized VCS %v", vcsType)
	}
	cmd.Dir = repoDir
	out, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("could not get VCS URL: %s", err)
	}

	cloneURL := strings.TrimSpace(string(out))
	if vcsType == "git" {
		cloneURL = strings.Replace(cloneURL, "git@github.com:", "git://github.com/", 1)
	}
	return cloneURL, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mingrammer/go-codelab/models"
)

// worker struct has necessary information to run sensor data producing job
type worker struct {
	ticker      *time.Ticker
	sensor      models.Sensor
	sensorError float64
	serverPort  int
}

// sensorWorker will be ran in goroutine to produce (random) sensor data and sends it to server
func sensorWorker(done <-chan struct{}, w worker) {
	for {
		select {
		case <-done:
			return
		case <-w.ticker.C:
			sensorData := w.sensor.GenerateSensorData(w.sensorError)
			url := getRequestServerURL(w.serverPort)

			fmt.Println(sensorData.SendingOutputString())

			sendJSONSensorData(url, sensorData)
		}
	}
}

func main() {
	numCPUs := runtime.NumCPU()
	runtime.GOMAXPROCS(numCPUs)

	var wg sync.WaitGroup
	const numWorkers = 3

	done := make(chan struct{})

	wg.Add(numWorkers)

	workerList := [numWorkers]worker{
		worker{
			ticker:      time.NewTicker(500 * time.Millisecond),
			sensor:      models.GyroSensor{},
			sensorError: 4.0,
			serverPort:  8001,
		},
		worker{
			ticker:      time.NewTicker(500 * time.Millisecond),
			sensor:      models.AccelSensor{},
			sensorError: 12.0,
			serverPort:  8002,
		},
		worker{
			ticker:      time.NewTicker(2 * time.Second),
			sensor:      models.TempSensor{},
			sensorError: 1.5,
			serverPort:  8003,
		},
	}

	for _, w := range workerList {
		go func(w worker) {
			sensorWorker(done, w)
			wg.Done()
		}(w)
	}

	wg.Wait()
}

// getRequestServerURL just concatenate the server url and port number to get complete server url
func getRequestServerURL(port int) string {
	urlComponents := []string{"http://127.0.0.1", strconv.Itoa(port)}

	return strings.Join(urlComponents, ":")
}

// sendJSONSensorData sends the sensor data as JSON type to server
func sendJSONSensorData(url string, sensorValues interface{}) {
	jsonBytes, err := json.Marshal(sensorValues)
	if err != nil {
		log.Fatal("Error occurs when mashaling the gyro sensor values")
	}

	buff := bytes.NewBuffer(jsonBytes)

	resp, err := http.Post(url, "application/json", buff)
	if err != nil || resp.StatusCode != 200 {
		log.Fatal("Error occurs when request the post data")
	}
}
<commit_msg>Create 'counter' to limit the sending request count<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mingrammer/go-codelab/models"
)

// worker struct has necessary information to run sensor data producing job
type worker struct {
	ticker      *time.Ticker
	sensor      models.Sensor
	sensorError float64
	serverPort  int
}

// counter is for counting the number of sending request
type counter struct {
	mutex sync.Mutex
	n     int
}

func (c *counter) count() {
	c.mutex.Lock()
	c.n++
	c.mutex.Unlock()
}

func (c *counter) value() int {
	return c.n
}

// sensorWorker will be ran in goroutine to produce (random) sensor data and sends it to server
func sensorWorker(done <-chan struct{}, w worker, c *counter) {
	for {
		select {
		case <-done:
			return
		case <-w.ticker.C:
			sensorData := w.sensor.GenerateSensorData(w.sensorError)
			url := getRequestServerURL(w.serverPort)

			fmt.Println(sensorData.SendingOutputString())

			sendJSONSensorData(url, sensorData)

			c.count()
		}
	}
}

func main() {
	numCPUs := runtime.NumCPU()
	runtime.GOMAXPROCS(numCPUs)

	var sendCounter counter
	var wg sync.WaitGroup
	const numWorkers = 3

	done := make(chan struct{})

	wg.Add(numWorkers)

	workerList := [numWorkers]worker{
		worker{
			ticker:      time.NewTicker(500 * time.Millisecond),
			sensor:      models.GyroSensor{},
			sensorError: 4.0,
			serverPort:  8001,
		},
		worker{
			ticker:      time.NewTicker(500 * time.Millisecond),
			sensor:      models.AccelSensor{},
			sensorError: 12.0,
			serverPort:  8002,
		},
		worker{
			ticker:      time.NewTicker(2 * time.Second),
			sensor:      models.TempSensor{},
			sensorError: 1.5,
			serverPort:  8003,
		},
	}

	for _, w := range workerList {
		go func(w worker) {
			sensorWorker(done, w, &sendCounter)
			wg.Done()
		}(w)
	}

	go func() {
		for {
			if sendCounter.value() > 100 {
				close(done)
				return
			}
		}
	}()

	wg.Wait()

	fmt.Printf("\n[Count : %d] Sending is stopped.\n", sendCounter.value())
}

// getRequestServerURL just concatenate the server url and port number to get complete server url
func getRequestServerURL(port int) string {
	urlComponents := []string{"http://127.0.0.1", strconv.Itoa(port)}

	return strings.Join(urlComponents, ":")
}

// sendJSONSensorData sends the sensor data as JSON type to server
func sendJSONSensorData(url string, sensorValues interface{}) {
	jsonBytes, err := json.Marshal(sensorValues)
	if err != nil {
		log.Fatal("Error occurs when mashaling the gyro sensor values")
	}

	buff := bytes.NewBuffer(jsonBytes)

	resp, err := http.Post(url, "application/json", buff)
	if err != nil || resp.StatusCode != 200 {
		log.Fatal("Error occurs when request the post data")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/koding/kite"
	"fmt"
	"os"
	"sync"
	"log"
	"github.com/bitly/go-nsq"
	"runtime"
        "github.com/nsabine/microservices/controller/controllerlib"
	"math"

)

var client *kite.Client
var k 	   *kite.Kite

func main() {
	fmt.Println("Starting Robber")

	k = kite.New("robber", "1.0.0")
	k.Config.Port = 6001
	k.Config.DisableAuthentication = true

        client = k.NewClient("http://" + os.Getenv("CONTROLLER_SERVICE_HOST")  + ":" + os.Getenv("CONTROLLER_SERVICE_PORT") + "/kite")
        connected, err := client.DialForever()
        if err != nil {
                k.Log.Fatal(err.Error())
        }

        // Wait until connected
        <-connected

	runtime.GOMAXPROCS(2)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go startKite()
	go startMessaging()
	wg.Wait()
}

func startKite() {
	k.HandleFunc("hello", hello)
	fmt.Println("Robber starting kite")
	k.Run()
}

func startMessaging() {
	fmt.Println("Robber configuring NSQ")

	config := nsq.NewConfig()
	q, _ := nsq.NewConsumer("tick", "ch", config)
	q.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		log.Printf("Got a message: %s", string(message.Body[:]))
		tick()
		return nil
	}))

	fmt.Println("Trying to connect to NSQ: " + os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")

	err := q.ConnectToNSQD(os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")
	if err != nil {
		log.Panic("Could not connect")
	}
	fmt.Println("Robber starting NSQ")
}

func hello(r *kite.Request) (interface{}, error) {

	fmt.Println("Robber got hello")

	// You can return anything as result, as long as it is JSON marshalable.
	return nil, nil
}

func tick() {
	result, err := client.Tell("getState")
        var GameState [][]controllerlib.UpdateRequest
        if err := result.Unmarshal(&GameState); err != nil {
                panic(err)
        }

	x,y := controllerlib.WhereAmI(GameState, controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: -1,
		YPos: -1,
	})

	me := controllerlib.UpdateRequest{
                MyName: os.Getenv("HOSTNAME"),
                Type: "Robber",
                XPos: x,
                YPos: y,
        }

        fmt.Println("I am here:", x, y)
	nearestCop := controllerlib.WhereNearest(GameState, me, "Cop")
	if nearestCop.XPos > me.XPos {
		x--
	} else {
		x++
	}
	if nearestCop.YPos > me.YPos {
		y--
	} else {
		y++
	}

	x = x + rand.Intn(1)
	y = y + rand.Intn(1)

	// make sure we didn't leave the board
	if x<0 {
		x = controllerlib.XSize-1
	}
	if y<0 {
		y = controllerlib.YSize-1
	}
	if x==controllerlib.XSize-1 {
		x = 0
	}
	if y==controllerlib.YSize-1 {
		y = 0
	}
	_, err = client.Tell("update", &controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: x,
		YPos: y,
	})
	if err != nil {
		panic(err)
	}
}
<commit_msg>random<commit_after>package main

import (
	"github.com/koding/kite"
	"fmt"
	"os"
	"sync"
	"log"
	"github.com/bitly/go-nsq"
	"runtime"
        "github.com/nsabine/microservices/controller/controllerlib"
	"math"

)

var client *kite.Client
var k 	   *kite.Kite

func main() {
	fmt.Println("Starting Robber")

	k = kite.New("robber", "1.0.0")
	k.Config.Port = 6001
	k.Config.DisableAuthentication = true

        client = k.NewClient("http://" + os.Getenv("CONTROLLER_SERVICE_HOST")  + ":" + os.Getenv("CONTROLLER_SERVICE_PORT") + "/kite")
        connected, err := client.DialForever()
        if err != nil {
                k.Log.Fatal(err.Error())
        }

        // Wait until connected
        <-connected

	runtime.GOMAXPROCS(2)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go startKite()
	go startMessaging()
	wg.Wait()
}

func startKite() {
	k.HandleFunc("hello", hello)
	fmt.Println("Robber starting kite")
	k.Run()
}

func startMessaging() {
	fmt.Println("Robber configuring NSQ")

	config := nsq.NewConfig()
	q, _ := nsq.NewConsumer("tick", "ch", config)
	q.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		log.Printf("Got a message: %s", string(message.Body[:]))
		tick()
		return nil
	}))

	fmt.Println("Trying to connect to NSQ: " + os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")

	err := q.ConnectToNSQD(os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")
	if err != nil {
		log.Panic("Could not connect")
	}
	fmt.Println("Robber starting NSQ")
}

func hello(r *kite.Request) (interface{}, error) {

	fmt.Println("Robber got hello")

	// You can return anything as result, as long as it is JSON marshalable.
	return nil, nil
}

func tick() {
	result, err := client.Tell("getState")
        var GameState [][]controllerlib.UpdateRequest
        if err := result.Unmarshal(&GameState); err != nil {
                panic(err)
        }

	x,y := controllerlib.WhereAmI(GameState, controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: -1,
		YPos: -1,
	})

	me := controllerlib.UpdateRequest{
                MyName: os.Getenv("HOSTNAME"),
                Type: "Robber",
                XPos: x,
                YPos: y,
        }

        fmt.Println("I am here:", x, y)
	nearestCop := controllerlib.WhereNearest(GameState, me, "Cop")
	if nearestCop.XPos > me.XPos {
		x--
	} else {
		x++
	}
	if nearestCop.YPos > me.YPos {
		y--
	} else {
		y++
	}

	x = x + math.rand.Intn(1)
	y = y + math.rand.Intn(1)

	// make sure we didn't leave the board
	if x<0 {
		x = controllerlib.XSize-1
	}
	if y<0 {
		y = controllerlib.YSize-1
	}
	if x==controllerlib.XSize-1 {
		x = 0
	}
	if y==controllerlib.YSize-1 {
		y = 0
	}
	_, err = client.Tell("update", &controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: x,
		YPos: y,
	})
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tag

import (
	"fmt"
	"io"
	"strconv"
)

// ID3v2Header is a type which represents an ID3v2 tag header.
type ID3v2Header struct {
	Version           Format
	Unsynchronisation bool
	ExtendedHeader    bool
	Experimental      bool
	Size              int
}

// readID3v2Header reads the ID3v2 header from the given io.Reader.
func readID3v2Header(r io.Reader) (*ID3v2Header, error) {
	b, err := readBytes(r, 10)
	if err != nil {
		return nil, fmt.Errorf("expected to read 10 bytes (ID3v2Header): %v", err)
	}

	if string(b[0:3]) != "ID3" {
		return nil, fmt.Errorf("expected to read \"ID3\"")
	}

	b = b[3:]
	var vers Format
	switch uint(b[0]) {
	case 2:
		vers = ID3v2_2
	case 3:
		vers = ID3v2_3
	case 4:
		vers = ID3v2_4
	case 0, 1:
		fallthrough
	default:
		return nil, fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0]))
	}

	// NB: We ignore b[1] (the revision) as we don't currently rely on it.
	return &ID3v2Header{
		Version:           vers,
		Unsynchronisation: getBit(b[2], 7),
		ExtendedHeader:    getBit(b[2], 6),
		Experimental:      getBit(b[2], 5),
		Size:              get7BitChunkedInt(b[3:7]),
	}, nil
}

// ID3v2FrameFlags is a type which represents the flags which can be set on an ID3v2 frame.
type ID3v2FrameFlags struct {
	// Message
	TagAlterPreservation  bool
	FileAlterPreservation bool
	ReadOnly              bool

	// Format
	GroupIdentity       bool
	Compression         bool
	Encryption          bool
	Unsynchronisation   bool
	DataLengthIndicator bool
}

func readID3v2FrameFlags(r io.Reader) (*ID3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &ID3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 6),
		FileAlterPreservation: getBit(msg, 5),
		ReadOnly:              getBit(msg, 4),
		GroupIdentity:         getBit(fmt, 7),
		Compression:           getBit(fmt, 3),
		Encryption:            getBit(fmt, 2),
		Unsynchronisation:     getBit(fmt, 1),
		DataLengthIndicator:   getBit(fmt, 0),
	}, nil
}

func readID3v2_2FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 3)
	if err != nil {
		return
	}
	size, err = readInt(r, 3)
	if err != nil {
		return
	}
	headerSize = 6
	return
}

func readID3v2_3FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = readInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

func readID3v2_4FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = read7BitChunkedInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

// readID3v2Frames reads ID3v2 frames from the given reader using the ID3v2Header.
func readID3v2Frames(r io.Reader, h *ID3v2Header) (map[string]interface{}, error) {
	offset := 10 // the size of the header
	result := make(map[string]interface{})

	for offset < h.Size {
		var err error
		var name string
		var size, headerSize int
		var flags *ID3v2FrameFlags

		switch h.Version {
		case ID3v2_2:
			name, size, headerSize, err = readID3v2_2FrameHeader(r)

		case ID3v2_3:
			name, size, headerSize, err = readID3v2_3FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v2FrameFlags(r)
			headerSize += 2

		case ID3v2_4:
			name, size, headerSize, err = readID3v2_4FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v2FrameFlags(r)
			headerSize += 2
		}

		if err != nil {
			return nil, err
		}

		// Avoid corrupted padding (see http://id3.org/Compliance%20Issues).
		if !validID3Frame(h.Version, name) {
			break
		}

		// FIXME: Do we still need this?
		// if size=0, we certainly are in a padding zone. ignore the rest of
		// the tags
		if size == 0 {
			break
		}

		offset += headerSize + size

		// Check this stuff out...
		if flags != nil && flags.DataLengthIndicator {
			_, err = read7BitChunkedInt(r, 4) // read 4
			if err != nil {
				return nil, err
			}
			size -= 4
		}

		if flags != nil && flags.Unsynchronisation {
			// FIXME: Implement this.
			continue
		}

		b, err := readBytes(r, size)
		if err != nil {
			return nil, err
		}

		// There can be multiple tag with the same name. Append a number to the
		// name if there is more than one.
		rawName := name
		if _, ok := result[rawName]; ok {
			for i := 0; ok; i++ {
				rawName = name + "_" + strconv.Itoa(i)
				_, ok = result[rawName]
			}
		}

		switch {
		case name == "TXXX" || name == "TXX":
			t, err := readTextWithDescrFrame(b, false, true) // no lang, but enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'T':
			txt, err := readTFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "UFID" || name == "UFI":
			t, err := readUFID(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "WXXX" || name == "WXX":
			t, err := readTextWithDescrFrame(b, false, false) // no lang, no enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'W':
			txt, err := readWFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "COMM" || name == "USLT":
			t, err := readTextWithDescrFrame(b, true, true) // both lang and enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "APIC":
			p, err := readAPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		case name == "PIC":
			p, err := readPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		default:
			result[rawName] = b
		}
	}
	return result, nil
}

type unsynchroniser struct {
	io.Reader
	ff bool
}

// filter io.Reader which skip the Unsynchronisation bytes
func (r *unsynchroniser) Read(p []byte) (int, error) {
	b := make([]byte, 1)
	i := 0
	for i < len(p) {
		if n, err := r.Reader.Read(b); err != nil || n == 0 {
			return i, err
		}
		if r.ff && b[0] == 0x00 {
			r.ff = false
			continue
		}
		p[i] = b[0]
		i++
		r.ff = (b[0] == 0xFF)
	}
	return i, nil
}

// ReadID3v2Tags parses ID3v2.{2,3,4} tags from the io.ReadSeeker into a Metadata, returning
// non-nil error on failure.
func ReadID3v2Tags(r io.ReadSeeker) (Metadata, error) {
	h, err := readID3v2Header(r)
	if err != nil {
		return nil, err
	}

	var ur io.Reader
	ur = r
	if h.Unsynchronisation {
		ur = &unsynchroniser{Reader: r}
	}

	f, err := readID3v2Frames(ur, h)
	if err != nil {
		return nil, err
	}
	return metadataID3v2{header: h, frames: f}, nil
}
<commit_msg>Correctly parse ID3 2.3 and 2.4 frame flags.<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tag

import (
	"fmt"
	"io"
	"strconv"
)

// ID3v2Header is a type which represents an ID3v2 tag header.
type ID3v2Header struct {
	Version           Format
	Unsynchronisation bool
	ExtendedHeader    bool
	Experimental      bool
	Size              int
}

// readID3v2Header reads the ID3v2 header from the given io.Reader.
func readID3v2Header(r io.Reader) (*ID3v2Header, error) {
	b, err := readBytes(r, 10)
	if err != nil {
		return nil, fmt.Errorf("expected to read 10 bytes (ID3v2Header): %v", err)
	}

	if string(b[0:3]) != "ID3" {
		return nil, fmt.Errorf("expected to read \"ID3\"")
	}

	b = b[3:]
	var vers Format
	switch uint(b[0]) {
	case 2:
		vers = ID3v2_2
	case 3:
		vers = ID3v2_3
	case 4:
		vers = ID3v2_4
	case 0, 1:
		fallthrough
	default:
		return nil, fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0]))
	}

	// NB: We ignore b[1] (the revision) as we don't currently rely on it.
	return &ID3v2Header{
		Version:           vers,
		Unsynchronisation: getBit(b[2], 7),
		ExtendedHeader:    getBit(b[2], 6),
		Experimental:      getBit(b[2], 5),
		Size:              get7BitChunkedInt(b[3:7]),
	}, nil
}

// ID3v2FrameFlags is a type which represents the flags which can be set on an ID3v2 frame.
type ID3v2FrameFlags struct {
	// Message (ID3 2.3.0 and 2.4.0)
	TagAlterPreservation  bool
	FileAlterPreservation bool
	ReadOnly              bool

	// Format (ID3 2.3.0 and 2.4.0)
	Compression   bool
	Encryption    bool
	GroupIdentity bool
	// ID3 2.4.0 only (see http://id3.org/id3v2.4.0-structure sec 4.1)
	Unsynchronisation   bool
	DataLengthIndicator bool
}

func readID3v23FrameFlags(r io.Reader) (*ID3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &ID3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 7),
		FileAlterPreservation: getBit(msg, 6),
		ReadOnly:              getBit(msg, 5),
		Compression:           getBit(fmt, 7),
		Encryption:            getBit(fmt, 6),
		GroupIdentity:         getBit(fmt, 5),
	}, nil
}

func readID3v24FrameFlags(r io.Reader) (*ID3v2FrameFlags, error) {
	b, err := readBytes(r, 2)
	if err != nil {
		return nil, err
	}

	msg := b[0]
	fmt := b[1]

	return &ID3v2FrameFlags{
		TagAlterPreservation:  getBit(msg, 6),
		FileAlterPreservation: getBit(msg, 5),
		ReadOnly:              getBit(msg, 4),
		GroupIdentity:         getBit(fmt, 6),
		Compression:           getBit(fmt, 3),
		Encryption:            getBit(fmt, 2),
		Unsynchronisation:     getBit(fmt, 1),
		DataLengthIndicator:   getBit(fmt, 0),
	}, nil

}

func readID3v2_2FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 3)
	if err != nil {
		return
	}
	size, err = readInt(r, 3)
	if err != nil {
		return
	}
	headerSize = 6
	return
}

func readID3v2_3FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = readInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

func readID3v2_4FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) {
	name, err = readString(r, 4)
	if err != nil {
		return
	}
	size, err = read7BitChunkedInt(r, 4)
	if err != nil {
		return
	}
	headerSize = 8
	return
}

// readID3v2Frames reads ID3v2 frames from the given reader using the ID3v2Header.
func readID3v2Frames(r io.Reader, h *ID3v2Header) (map[string]interface{}, error) {
	offset := 10 // the size of the header
	result := make(map[string]interface{})

	for offset < h.Size {
		var err error
		var name string
		var size, headerSize int
		var flags *ID3v2FrameFlags

		switch h.Version {
		case ID3v2_2:
			name, size, headerSize, err = readID3v2_2FrameHeader(r)

		case ID3v2_3:
			name, size, headerSize, err = readID3v2_3FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v23FrameFlags(r)
			headerSize += 2

		case ID3v2_4:
			name, size, headerSize, err = readID3v2_4FrameHeader(r)
			if err != nil {
				return nil, err
			}
			flags, err = readID3v24FrameFlags(r)
			headerSize += 2
		}

		if err != nil {
			return nil, err
		}

		// Avoid corrupted padding (see http://id3.org/Compliance%20Issues).
		if !validID3Frame(h.Version, name) {
			break
		}

		// FIXME: Do we still need this?
		// if size=0, we certainly are in a padding zone. ignore the rest of
		// the tags
		if size == 0 {
			break
		}

		offset += headerSize + size

		if flags != nil {
			if flags.Compression {
				_, err = read7BitChunkedInt(r, 4) // read 4
				if err != nil {
					return nil, err
				}
				size -= 4
			}

			if flags.Encryption {
				_, err = readBytes(r, 1) // read 1 byte of encryption method
				if err != nil {
					return nil, err
				}
				size -= 1
			}
		}

		b, err := readBytes(r, size)
		if err != nil {
			return nil, err
		}

		// There can be multiple tag with the same name. Append a number to the
		// name if there is more than one.
		rawName := name
		if _, ok := result[rawName]; ok {
			for i := 0; ok; i++ {
				rawName = name + "_" + strconv.Itoa(i)
				_, ok = result[rawName]
			}
		}

		switch {
		case name == "TXXX" || name == "TXX":
			t, err := readTextWithDescrFrame(b, false, true) // no lang, but enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'T':
			txt, err := readTFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "UFID" || name == "UFI":
			t, err := readUFID(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "WXXX" || name == "WXX":
			t, err := readTextWithDescrFrame(b, false, false) // no lang, no enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name[0] == 'W':
			txt, err := readWFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = txt

		case name == "COMM" || name == "USLT":
			t, err := readTextWithDescrFrame(b, true, true) // both lang and enc
			if err != nil {
				return nil, err
			}
			result[rawName] = t

		case name == "APIC":
			p, err := readAPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		case name == "PIC":
			p, err := readPICFrame(b)
			if err != nil {
				return nil, err
			}
			result[rawName] = p

		default:
			result[rawName] = b
		}
	}
	return result, nil
}

type unsynchroniser struct {
	io.Reader
	ff bool
}

// filter io.Reader which skip the Unsynchronisation bytes
func (r *unsynchroniser) Read(p []byte) (int, error) {
	b := make([]byte, 1)
	i := 0
	for i < len(p) {
		if n, err := r.Reader.Read(b); err != nil || n == 0 {
			return i, err
		}
		if r.ff && b[0] == 0x00 {
			r.ff = false
			continue
		}
		p[i] = b[0]
		i++
		r.ff = (b[0] == 0xFF)
	}
	return i, nil
}

// ReadID3v2Tags parses ID3v2.{2,3,4} tags from the io.ReadSeeker into a Metadata, returning
// non-nil error on failure.
func ReadID3v2Tags(r io.ReadSeeker) (Metadata, error) {
	h, err := readID3v2Header(r)
	if err != nil {
		return nil, err
	}

	var ur io.Reader
	ur = r
	if h.Unsynchronisation {
		ur = &unsynchroniser{Reader: r}
	}

	f, err := readID3v2Frames(ur, h)
	if err != nil {
		return nil, err
	}
	return metadataID3v2{header: h, frames: f}, nil
}
<|endoftext|>