<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package cmd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/lfs"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/pprof"
	"code.gitea.io/gitea/modules/private"
	"code.gitea.io/gitea/modules/setting"

	"github.com/dgrijalva/jwt-go"
	"github.com/kballard/go-shellquote"
	"github.com/unknwon/com"
	"github.com/urfave/cli"
)

const (
	lfsAuthenticateVerb = "git-lfs-authenticate"
)

// CmdServ represents the available serv sub-command.
var CmdServ = cli.Command{
	Name:        "serv",
	Usage:       "This command should only be called by SSH shell",
	Description: `Serv provide access auth for repositories`,
	Action:      runServ,
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name: "enable-pprof",
		},
		cli.BoolFlag{
			Name: "debug",
		},
	},
}

func setup(logPath string, debug bool) {
	_ = log.DelLogger("console")
	if debug {
		_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
	}
	setting.NewContext()
	if debug {
		setting.ProdMode = false
	}
}

var (
	allowedCommands = map[string]models.AccessMode{
		"git-upload-pack":    models.AccessModeRead,
		"git-upload-archive": models.AccessModeRead,
		"git-receive-pack":   models.AccessModeWrite,
		lfsAuthenticateVerb:  models.AccessModeNone,
	}
	alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
)

func fail(userMessage, logMessage string, args ...interface{}) {
	fmt.Fprintln(os.Stderr, "Gitea:", userMessage)

	if len(logMessage) > 0 {
		if !setting.ProdMode {
			fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
		}
	}

	os.Exit(1)
}

func runServ(c *cli.Context) error {
	// FIXME: This needs to internationalised
	setup("serv.log", c.Bool("debug"))

	if setting.SSH.Disabled {
		println("Gitea: SSH has been disabled")
		return nil
	}

	if len(c.Args()) < 1 {
		if err := cli.ShowSubcommandHelp(c); err != nil {
			fmt.Printf("error showing subcommand help: %v\n", err)
		}
		return nil
	}

	keys := strings.Split(c.Args()[0], "-")
	if len(keys) != 2 || keys[0] != "key" {
		fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
	}
	keyID := com.StrTo(keys[1]).MustInt64()

	cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
	if len(cmd) == 0 {
		key, user, err := private.ServNoCommand(keyID)
		if err != nil {
			fail("Internal error", "Failed to check provided key: %v", err)
		}
		if key.Type == models.KeyTypeDeploy {
			println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
		} else {
			println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
		}
		println("If this is unexpected, please log in with password and setup Gitea under another user.")
		return nil
	} else if c.Bool("debug") {
		log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
	}

	words, err := shellquote.Split(cmd)
	if err != nil {
		fail("Error parsing arguments", "Failed to parse arguments: %v", err)
	}

	if len(words) < 2 {
		fail("Too few arguments", "Too few arguments in cmd: %s", cmd)
	}

	verb := words[0]
	repoPath := words[1]
	if repoPath[0] == '/' {
		repoPath = repoPath[1:]
	}

	var lfsVerb string
	if verb == lfsAuthenticateVerb {
		if !setting.LFS.StartServer {
			fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
		}

		if len(words) > 2 {
			lfsVerb = words[2]
		}
	}

	// LowerCase and trim the repoPath as that's how they are stored.
	repoPath = strings.ToLower(strings.TrimSpace(repoPath))

	rr := strings.SplitN(repoPath, "/", 2)
	if len(rr) != 2 {
		fail("Invalid repository path", "Invalid repository path: %v", repoPath)
	}

	username := strings.ToLower(rr[0])
	reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))

	if alphaDashDotPattern.MatchString(reponame) {
		fail("Invalid repo name", "Invalid repo name: %s", reponame)
	}

	if setting.EnablePprof || c.Bool("enable-pprof") {
		if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
			fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
		}

		stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
		if err != nil {
			fail("Internal Server Error", "Unable to start CPU profile: %v", err)
		}
		defer func() {
			stopCPUProfiler()
			err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
			if err != nil {
				fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
			}
		}()
	}

	requestedMode, has := allowedCommands[verb]
	if !has {
		fail("Unknown git command", "Unknown git command %s", verb)
	}

	if verb == lfsAuthenticateVerb {
		if lfsVerb == "upload" {
			requestedMode = models.AccessModeWrite
		} else if lfsVerb == "download" {
			requestedMode = models.AccessModeRead
		} else {
			fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
		}
	}

	results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
	if err != nil {
		if private.IsErrServCommand(err) {
			errServCommand := err.(private.ErrServCommand)
			if errServCommand.StatusCode != http.StatusInternalServerError {
				fail("Unauthorized", "%s", errServCommand.Error())
			} else {
				fail("Internal Server Error", "%s", errServCommand.Error())
			}
		}
		fail("Internal Server Error", "%s", err.Error())
	}
	os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
	os.Setenv(models.EnvRepoName, results.RepoName)
	os.Setenv(models.EnvRepoUsername, results.OwnerName)
	os.Setenv(models.EnvPusherName, results.UserName)
	os.Setenv(models.EnvPusherEmail, results.UserEmail)
	os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
	os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
	os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
	os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
	os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
	os.Setenv(models.EnvAppURL, setting.AppURL)

	//LFS token authentication
	if verb == lfsAuthenticateVerb {
		url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))

		now := time.Now()
		claims := lfs.Claims{
			StandardClaims: jwt.StandardClaims{
				ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
				NotBefore: now.Unix(),
			},
			RepoID: results.RepoID,
			Op:     lfsVerb,
			UserID: results.UserID,
		}
		token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

		// Sign and get the complete encoded token as a string using the secret
		tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
		if err != nil {
			fail("Internal error", "Failed to sign JWT token: %v", err)
		}

		tokenAuthentication := &models.LFSTokenResponse{
			Header: make(map[string]string),
			Href:   url,
		}
		tokenAuthentication.Header["Authorization"] = fmt.Sprintf("Bearer %s", tokenString)

		enc := json.NewEncoder(os.Stdout)
		err = enc.Encode(tokenAuthentication)
		if err != nil {
			fail("Internal error", "Failed to encode LFS json response: %v", err)
		}
		return nil
	}

	// Special handle for Windows.
	if setting.IsWindows {
		verb = strings.Replace(verb, "-", " ", 1)
	}

	var gitcmd *exec.Cmd
	verbs := strings.Split(verb, " ")
	if len(verbs) == 2 {
		gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
	} else {
		gitcmd = exec.Command(verb, repoPath)
	}

	gitcmd.Dir = setting.RepoRootPath
	gitcmd.Stdout = os.Stdout
	gitcmd.Stdin = os.Stdin
	gitcmd.Stderr = os.Stderr
	if err = gitcmd.Run(); err != nil {
		fail("Internal error", "Failed to execute git command: %v", err)
	}

	// Update user key activity.
	if results.KeyID > 0 {
		if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
			fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
		}
	}

	return nil
}
<commit_msg>Gitea serv, hooks, manager and the like should always display Fatals (#13032)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package cmd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/lfs"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/pprof"
	"code.gitea.io/gitea/modules/private"
	"code.gitea.io/gitea/modules/setting"

	"github.com/dgrijalva/jwt-go"
	"github.com/kballard/go-shellquote"
	"github.com/unknwon/com"
	"github.com/urfave/cli"
)

const (
	lfsAuthenticateVerb = "git-lfs-authenticate"
)

// CmdServ represents the available serv sub-command.
var CmdServ = cli.Command{
	Name:        "serv",
	Usage:       "This command should only be called by SSH shell",
	Description: `Serv provide access auth for repositories`,
	Action:      runServ,
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name: "enable-pprof",
		},
		cli.BoolFlag{
			Name: "debug",
		},
	},
}

func setup(logPath string, debug bool) {
	_ = log.DelLogger("console")
	if debug {
		_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
	} else {
		_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
	}
	setting.NewContext()
	if debug {
		setting.ProdMode = false
	}
}

var (
	allowedCommands = map[string]models.AccessMode{
		"git-upload-pack":    models.AccessModeRead,
		"git-upload-archive": models.AccessModeRead,
		"git-receive-pack":   models.AccessModeWrite,
		lfsAuthenticateVerb:  models.AccessModeNone,
	}
	alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
)

func fail(userMessage, logMessage string, args ...interface{}) {
	fmt.Fprintln(os.Stderr, "Gitea:", userMessage)

	if len(logMessage) > 0 {
		if !setting.ProdMode {
			fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
		}
	}

	os.Exit(1)
}

func runServ(c *cli.Context) error {
	// FIXME: This needs to internationalised
	setup("serv.log", c.Bool("debug"))

	if setting.SSH.Disabled {
		println("Gitea: SSH has been disabled")
		return nil
	}

	if len(c.Args()) < 1 {
		if err := cli.ShowSubcommandHelp(c); err != nil {
			fmt.Printf("error showing subcommand help: %v\n", err)
		}
		return nil
	}

	keys := strings.Split(c.Args()[0], "-")
	if len(keys) != 2 || keys[0] != "key" {
		fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
	}
	keyID := com.StrTo(keys[1]).MustInt64()

	cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
	if len(cmd) == 0 {
		key, user, err := private.ServNoCommand(keyID)
		if err != nil {
			fail("Internal error", "Failed to check provided key: %v", err)
		}
		if key.Type == models.KeyTypeDeploy {
			println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
		} else {
			println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
		}
		println("If this is unexpected, please log in with password and setup Gitea under another user.")
		return nil
	} else if c.Bool("debug") {
		log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
	}

	words, err := shellquote.Split(cmd)
	if err != nil {
		fail("Error parsing arguments", "Failed to parse arguments: %v", err)
	}

	if len(words) < 2 {
		fail("Too few arguments", "Too few arguments in cmd: %s", cmd)
	}

	verb := words[0]
	repoPath := words[1]
	if repoPath[0] == '/' {
		repoPath = repoPath[1:]
	}

	var lfsVerb string
	if verb == lfsAuthenticateVerb {
		if !setting.LFS.StartServer {
			fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
		}

		if len(words) > 2 {
			lfsVerb = words[2]
		}
	}

	// LowerCase and trim the repoPath as that's how they are stored.
	repoPath = strings.ToLower(strings.TrimSpace(repoPath))

	rr := strings.SplitN(repoPath, "/", 2)
	if len(rr) != 2 {
		fail("Invalid repository path", "Invalid repository path: %v", repoPath)
	}

	username := strings.ToLower(rr[0])
	reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))

	if alphaDashDotPattern.MatchString(reponame) {
		fail("Invalid repo name", "Invalid repo name: %s", reponame)
	}

	if setting.EnablePprof || c.Bool("enable-pprof") {
		if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
			fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
		}

		stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
		if err != nil {
			fail("Internal Server Error", "Unable to start CPU profile: %v", err)
		}
		defer func() {
			stopCPUProfiler()
			err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
			if err != nil {
				fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
			}
		}()
	}

	requestedMode, has := allowedCommands[verb]
	if !has {
		fail("Unknown git command", "Unknown git command %s", verb)
	}

	if verb == lfsAuthenticateVerb {
		if lfsVerb == "upload" {
			requestedMode = models.AccessModeWrite
		} else if lfsVerb == "download" {
			requestedMode = models.AccessModeRead
		} else {
			fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
		}
	}

	results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
	if err != nil {
		if private.IsErrServCommand(err) {
			errServCommand := err.(private.ErrServCommand)
			if errServCommand.StatusCode != http.StatusInternalServerError {
				fail("Unauthorized", "%s", errServCommand.Error())
			} else {
				fail("Internal Server Error", "%s", errServCommand.Error())
			}
		}
		fail("Internal Server Error", "%s", err.Error())
	}
	os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
	os.Setenv(models.EnvRepoName, results.RepoName)
	os.Setenv(models.EnvRepoUsername, results.OwnerName)
	os.Setenv(models.EnvPusherName, results.UserName)
	os.Setenv(models.EnvPusherEmail, results.UserEmail)
	os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
	os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
	os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
	os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
	os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
	os.Setenv(models.EnvAppURL, setting.AppURL)

	//LFS token authentication
	if verb == lfsAuthenticateVerb {
		url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))

		now := time.Now()
		claims := lfs.Claims{
			StandardClaims: jwt.StandardClaims{
				ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
				NotBefore: now.Unix(),
			},
			RepoID: results.RepoID,
			Op:     lfsVerb,
			UserID: results.UserID,
		}
		token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

		// Sign and get the complete encoded token as a string using the secret
		tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
		if err != nil {
			fail("Internal error", "Failed to sign JWT token: %v", err)
		}

		tokenAuthentication := &models.LFSTokenResponse{
			Header: make(map[string]string),
			Href:   url,
		}
		tokenAuthentication.Header["Authorization"] = fmt.Sprintf("Bearer %s", tokenString)

		enc := json.NewEncoder(os.Stdout)
		err = enc.Encode(tokenAuthentication)
		if err != nil {
			fail("Internal error", "Failed to encode LFS json response: %v", err)
		}
		return nil
	}

	// Special handle for Windows.
	if setting.IsWindows {
		verb = strings.Replace(verb, "-", " ", 1)
	}

	var gitcmd *exec.Cmd
	verbs := strings.Split(verb, " ")
	if len(verbs) == 2 {
		gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
	} else {
		gitcmd = exec.Command(verb, repoPath)
	}

	gitcmd.Dir = setting.RepoRootPath
	gitcmd.Stdout = os.Stdout
	gitcmd.Stdin = os.Stdin
	gitcmd.Stderr = os.Stderr
	if err = gitcmd.Run(); err != nil {
		fail("Internal error", "Failed to execute git command: %v", err)
	}

	// Update user key activity.
	if results.KeyID > 0 {
		if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
			fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build linux

package cgroups

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

	"github.com/docker/docker/pkg/mount"
	"github.com/docker/docker/pkg/units"
)

const cgroupNamePrefix = "name="

// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
func FindCgroupMountpoint(subsystem string) (string, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return "", err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		fields := strings.Split(txt, " ")
		for _, opt := range strings.Split(fields[len(fields)-1], ",") {
			if opt == subsystem {
				return fields[4], nil
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	return "", NewNotFoundError(subsystem)
}

func FindCgroupMountpointAndSource(subsystem string) (string, string, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return "", "", err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		fields := strings.Split(txt, " ")
		for _, opt := range strings.Split(fields[len(fields)-1], ",") {
			if opt == subsystem {
				return fields[4], fields[3], nil
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return "", "", err
	}

	return "", "", NewNotFoundError(subsystem)
}

func FindCgroupMountpointDir() (string, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return "", err
	}

	for _, mount := range mounts {
		if mount.Fstype == "cgroup" {
			return filepath.Dir(mount.Mountpoint), nil
		}
	}

	return "", NewNotFoundError("cgroup")
}

type Mount struct {
	Mountpoint string
	Root       string
	Subsystems []string
}

func (m Mount) GetThisCgroupDir() (string, error) {
	if len(m.Subsystems) == 0 {
		return "", fmt.Errorf("no subsystem for mount")
	}

	return GetThisCgroupDir(m.Subsystems[0])
}

func GetCgroupMounts() ([]Mount, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	all, err := GetAllSubsystems()
	if err != nil {
		return nil, err
	}

	allMap := make(map[string]bool)
	for _, s := range all {
		allMap[s] = true
	}

	res := []Mount{}
	for _, mount := range mounts {
		if mount.Fstype == "cgroup" {
			m := Mount{Mountpoint: mount.Mountpoint, Root: mount.Root}

			for _, opt := range strings.Split(mount.VfsOpts, ",") {
				if strings.HasPrefix(opt, cgroupNamePrefix) {
					m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
				}
				if allMap[opt] {
					m.Subsystems = append(m.Subsystems, opt)
				}
			}
			res = append(res, m)
		}
	}
	return res, nil
}

// Returns all the cgroup subsystems supported by the kernel
func GetAllSubsystems() ([]string, error) {
	f, err := os.Open("/proc/cgroups")
	if err != nil {
		return nil, err
	}
	defer f.Close()

	subsystems := []string{}

	s := bufio.NewScanner(f)
	for s.Scan() {
		if err := s.Err(); err != nil {
			return nil, err
		}
		text := s.Text()
		if text[0] != '#' {
			parts := strings.Fields(text)
			if len(parts) >= 4 && parts[3] != "0" {
				subsystems = append(subsystems, parts[0])
			}
		}
	}
	return subsystems, nil
}

// Returns the relative path to the cgroup docker is running in.
func GetThisCgroupDir(subsystem string) (string, error) {
	f, err := os.Open("/proc/self/cgroup")
	if err != nil {
		return "", err
	}
	defer f.Close()

	return ParseCgroupFile(subsystem, f)
}

func GetInitCgroupDir(subsystem string) (string, error) {
	f, err := os.Open("/proc/1/cgroup")
	if err != nil {
		return "", err
	}
	defer f.Close()

	return ParseCgroupFile(subsystem, f)
}

func ReadProcsFile(dir string) ([]int, error) {
	f, err := os.Open(filepath.Join(dir, "cgroup.procs"))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var (
		s   = bufio.NewScanner(f)
		out = []int{}
	)

	for s.Scan() {
		if t := s.Text(); t != "" {
			pid, err := strconv.Atoi(t)
			if err != nil {
				return nil, err
			}
			out = append(out, pid)
		}
	}
	return out, nil
}

func ParseCgroupFile(subsystem string, r io.Reader) (string, error) {
	s := bufio.NewScanner(r)

	for s.Scan() {
		if err := s.Err(); err != nil {
			return "", err
		}

		text := s.Text()
		parts := strings.Split(text, ":")

		for _, subs := range strings.Split(parts[1], ",") {
			if subs == subsystem || subs == cgroupNamePrefix+subsystem {
				return parts[2], nil
			}
		}
	}

	return "", NewNotFoundError(subsystem)
}

func PathExists(path string) bool {
	if _, err := os.Stat(path); err != nil {
		return false
	}
	return true
}

func EnterPid(cgroupPaths map[string]string, pid int) error {
	for _, path := range cgroupPaths {
		if PathExists(path) {
			if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
				[]byte(strconv.Itoa(pid)), 0700); err != nil {
				return err
			}
		}
	}
	return nil
}

// RemovePaths iterates over the provided paths removing them.
// We trying to remove all paths five times with increasing delay between tries.
// If after all there are not removed cgroups - appropriate error will be
// returned.
func RemovePaths(paths map[string]string) (err error) {
	delay := 10 * time.Millisecond
	for i := 0; i < 5; i++ {
		if i != 0 {
			time.Sleep(delay)
			delay *= 2
		}
		for s, p := range paths {
			os.RemoveAll(p)
			// TODO: here probably should be logging
			_, err := os.Stat(p)
			// We need this strange way of checking cgroups existence because
			// RemoveAll almost always returns error, even on already removed
			// cgroups
			if os.IsNotExist(err) {
				delete(paths, s)
			}
		}
		if len(paths) == 0 {
			return nil
		}
	}
	return fmt.Errorf("Failed to remove paths: %s", paths)
}

func GetHugePageSize() ([]string, error) {
	var pageSizes []string
	sizeList := []string{"B", "kB", "MB", "GB", "TB", "PB"}
	files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
	if err != nil {
		return pageSizes, err
	}
	for _, st := range files {
		nameArray := strings.Split(st.Name(), "-")
		pageSize, err := units.RAMInBytes(nameArray[1])
		if err != nil {
			return []string{}, err
		}
		sizeString := units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList)
		pageSizes = append(pageSizes, sizeString)
	}

	return pageSizes, nil
}
<commit_msg>Parse directly in FindCgroupMountpointDir<commit_after>// +build linux

package cgroups

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

	"github.com/docker/docker/pkg/mount"
	"github.com/docker/docker/pkg/units"
)

const cgroupNamePrefix = "name="

// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
func FindCgroupMountpoint(subsystem string) (string, error) {
	// We are not using mount.GetMounts() because it's super-inefficient,
	// parsing it directly sped up x10 times because of not using Sscanf.
	// It was one of two major performance drawbacks in container start.
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return "", err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		fields := strings.Split(txt, " ")
		for _, opt := range strings.Split(fields[len(fields)-1], ",") {
			if opt == subsystem {
				return fields[4], nil
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	return "", NewNotFoundError(subsystem)
}

func FindCgroupMountpointAndSource(subsystem string) (string, string, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return "", "", err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		fields := strings.Split(txt, " ")
		for _, opt := range strings.Split(fields[len(fields)-1], ",") {
			if opt == subsystem {
				return fields[4], fields[3], nil
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return "", "", err
	}

	return "", "", NewNotFoundError(subsystem)
}

func FindCgroupMountpointDir() (string, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return "", err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		fields := strings.Split(txt, " ")
		if fields[7] == "cgroup" {
			return filepath.Dir(fields[4]), nil
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	return "", NewNotFoundError("cgroup")
}

type Mount struct {
	Mountpoint string
	Root       string
	Subsystems []string
}

func (m Mount) GetThisCgroupDir() (string, error) {
	if len(m.Subsystems) == 0 {
		return "", fmt.Errorf("no subsystem for mount")
	}

	return GetThisCgroupDir(m.Subsystems[0])
}

func GetCgroupMounts() ([]Mount, error) {
	mounts, err := mount.GetMounts()
	if err != nil {
		return nil, err
	}

	all, err := GetAllSubsystems()
	if err != nil {
		return nil, err
	}

	allMap := make(map[string]bool)
	for _, s := range all {
		allMap[s] = true
	}

	res := []Mount{}
	for _, mount := range mounts {
		if mount.Fstype == "cgroup" {
			m := Mount{Mountpoint: mount.Mountpoint, Root: mount.Root}

			for _, opt := range strings.Split(mount.VfsOpts, ",") {
				if strings.HasPrefix(opt, cgroupNamePrefix) {
					m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
				}
				if allMap[opt] {
					m.Subsystems = append(m.Subsystems, opt)
				}
			}
			res = append(res, m)
		}
	}
	return res, nil
}

// Returns all the cgroup subsystems supported by the kernel
func GetAllSubsystems() ([]string, error) {
	f, err := os.Open("/proc/cgroups")
	if err != nil {
		return nil, err
	}
	defer f.Close()

	subsystems := []string{}

	s := bufio.NewScanner(f)
	for s.Scan() {
		if err := s.Err(); err != nil {
			return nil, err
		}
		text := s.Text()
		if text[0] != '#' {
			parts := strings.Fields(text)
			if len(parts) >= 4 && parts[3] != "0" {
				subsystems = append(subsystems, parts[0])
			}
		}
	}
	return subsystems, nil
}

// Returns the relative path to the cgroup docker is running in.
func GetThisCgroupDir(subsystem string) (string, error) {
	f, err := os.Open("/proc/self/cgroup")
	if err != nil {
		return "", err
	}
	defer f.Close()

	return ParseCgroupFile(subsystem, f)
}

func GetInitCgroupDir(subsystem string) (string, error) {
	f, err := os.Open("/proc/1/cgroup")
	if err != nil {
		return "", err
	}
	defer f.Close()

	return ParseCgroupFile(subsystem, f)
}

func ReadProcsFile(dir string) ([]int, error) {
	f, err := os.Open(filepath.Join(dir, "cgroup.procs"))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var (
		s   = bufio.NewScanner(f)
		out = []int{}
	)

	for s.Scan() {
		if t := s.Text(); t != "" {
			pid, err := strconv.Atoi(t)
			if err != nil {
				return nil, err
			}
			out = append(out, pid)
		}
	}
	return out, nil
}

func ParseCgroupFile(subsystem string, r io.Reader) (string, error) {
	s := bufio.NewScanner(r)

	for s.Scan() {
		if err := s.Err(); err != nil {
			return "", err
		}

		text := s.Text()
		parts := strings.Split(text, ":")

		for _, subs := range strings.Split(parts[1], ",") {
			if subs == subsystem || subs == cgroupNamePrefix+subsystem {
				return parts[2], nil
			}
		}
	}

	return "", NewNotFoundError(subsystem)
}

func PathExists(path string) bool {
	if _, err := os.Stat(path); err != nil {
		return false
	}
	return true
}

func EnterPid(cgroupPaths map[string]string, pid int) error {
	for _, path := range cgroupPaths {
		if PathExists(path) {
			if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
				[]byte(strconv.Itoa(pid)), 0700); err != nil {
				return err
			}
		}
	}
	return nil
}

// RemovePaths iterates over the provided paths removing them.
// We trying to remove all paths five times with increasing delay between tries.
// If after all there are not removed cgroups - appropriate error will be
// returned.
func RemovePaths(paths map[string]string) (err error) {
	delay := 10 * time.Millisecond
	for i := 0; i < 5; i++ {
		if i != 0 {
			time.Sleep(delay)
			delay *= 2
		}
		for s, p := range paths {
			os.RemoveAll(p)
			// TODO: here probably should be logging
			_, err := os.Stat(p)
			// We need this strange way of checking cgroups existence because
			// RemoveAll almost always returns error, even on already removed
			// cgroups
			if os.IsNotExist(err) {
				delete(paths, s)
			}
		}
		if len(paths) == 0 {
			return nil
		}
	}
	return fmt.Errorf("Failed to remove paths: %s", paths)
}

func GetHugePageSize() ([]string, error) {
	var pageSizes []string
	sizeList := []string{"B", "kB", "MB", "GB", "TB", "PB"}
	files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
	if err != nil {
		return pageSizes, err
	}
	for _, st := range files {
		nameArray := strings.Split(st.Name(), "-")
		pageSize, err := units.RAMInBytes(nameArray[1])
		if err != nil {
			return []string{}, err
		}
		sizeString := units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList)
		pageSizes = append(pageSizes, sizeString)
	}

	return pageSizes, nil
}
<|endoftext|>


<commit_before>package dependency

import (
	"fmt"
	"strings"
	"sync"

	"github.com/andygrunwald/perseus/dependency/repository"
	"github.com/andygrunwald/perseus/types/set"
)

// ComposerResolver is an implementation of Resolver for Composer (PHP)
type ComposerResolver struct {
	// repository is the Client to talk to a specific endpoint (e.g. Packagist)
	repository repository.Client

	// workerCount is the number of worker that will be started
	workerCount int
	waitGroup   sync.WaitGroup
	// queue is the channel where all jobs are stored that needs to be processed by the worker
	queue chan *Package
	// results is the channel where all resolved dependencies will be streamed
	results chan *Result
	// resolved is a storage to track which packages are already resolved
	resolved *set.Set
	// queued is a storage to track which packages were already queued
	queued *set.Set
	// replacee is a hashmap to replace old/renamed/obsolete packages that would throw an error otherwise
	replacee map[string]string
}

// GetResultStream will return the channel for results.
// During the process of resolving dependencies, this channel will be filled
// with the results. Those can be processed next to the resolve process.
func (d *ComposerResolver) GetResultStream() <-chan *Result {
	return d.results
}

// Resolve will start of the dependency resolver process.
func (d *ComposerResolver) Resolve(packageList []*Package) {
	d.startWorker()

	// Queue packages
	for _, p := range packageList {
		d.queuePackage(p)
	}

	// Wait until all packages are resolved and close everything
	d.waitGroup.Wait()
	close(d.queue)
	close(d.results)
}

// QueuePackage adds package p to the queue
func (d *ComposerResolver) queuePackage(p *Package) {
	d.waitGroup.Add(1)
	d.markAsQueued(p.Name)
	d.queue <- p
}

// startWorker will boot up the worker routines
func (d *ComposerResolver) startWorker() {
	for w := 1; w <= d.workerCount; w++ {
		go d.worker(w, d.queue, d.results)
	}
}

// worker is a single worker routine. This worker will be launched multiple times to work on
// the queue as efficient as possible.
// id is a unique number assigned per worker (only for logging/debugging purpose).
// jobs is the jobs channel. The worker needs to be able to add more jobs to the queue as well.
// results is the channel where all results will be stored once they are resolved.
func (d *ComposerResolver) worker(id int, jobs chan<- *Package, results chan<- *Result) {
	// Worker has started. Lets do the hard work. Gimme the jobs.
	for j := range d.queue {
		packageName := j.Name

		// We don't need to process system packages.
		// System packages (like php or ext-curl) needs to be fulfilled by the system.
		// Not by the ApiClient
		if d.isSystemPackage(packageName) {
			d.waitGroup.Done()
			continue
		}

		// We overwrite specific packages, because they are added as dependencies to some older tags.
		// And those was renamed (for some reasons). But we are scanning all tags / branches.
		if r, ok := d.replacee[packageName]; ok {
			packageName = r
		}

		// Get information about the package from ApiClient
		p, resp, err := d.repository.GetPackageByName(packageName)
		if err != nil {
			// API Call error here. Request to Packagist failed
			r := &Result{
				Package: j,
				Error:   fmt.Errorf("API returned status code %d: %s", resp.StatusCode, err),
			}
			results <- r
			d.waitGroup.Done()
			continue
		}
		// Check if we got information from Packagist.
		// Maybe no error was thrown, but no package comes with the payload.
		// For us, as a dependency resolver, this is equal an error.
		if p == nil {
			// API Call error here. No package received from Packagist
			r := &Result{
				Package: j,
				Error:   fmt.Errorf("API Call to Packagist successful (Status code %d), but no package received", resp.StatusCode),
			}
			results <- r
			d.waitGroup.Done()
			continue
		}

		// Now we got the package.
		// Let us determine all requirements / dependencies from all versions,
		// because those packages needs to be resolved as well
		for _, version := range p.Versions {
			// If we don` have required packaged, we can handle the next one
			if len(version.Require) == 0 {
				continue
			}

			// Handle dependency per dependency
			for dependency := range version.Require {
				// We check if this dependency was already queued.
				// It is typical that many different versions of one package don't
				// change dependencies so often. So we would queue one package
				// multiple times. With this small check we save a lot of work here.
				if d.shouldPackageBeQueued(dependency) {
					d.markAsQueued(dependency)

					packageToResolve, _ := NewPackage(dependency, "")
					// We add two additional waitgroup entries here.
					// You might ask why? Regularly we add a new entry when we have a new package.
					// Here we add two, because of a) the new package and b) the new queue
					// entry of the package. We queue the package in a new go routine to
					// avoid a blocking state here. But we need to know when this go routine
					// is finished. So we observice this "Add package to queue" go routine
					// with the same waitgroup.
					d.waitGroup.Add(2)
					go func() {
						jobs <- packageToResolve
						d.waitGroup.Done()
					}()
				}
			}
		}

		// Package was resolved. Lets do everything which is necessary to change this package to a result.
		resolvedPackage, err := NewPackage(p.Name, p.Repository)
		r := &Result{
			Package: resolvedPackage,
			Error:   err,
		}
		results <- r
		d.waitGroup.Done()
		d.markAsResolved(p.Name)
	}
}

// markAsResolved will mark package p as resolved.
func (d *ComposerResolver) markAsResolved(p string) {
	d.resolved.Add(p)
}

// markAsQueued will mark package p as queued.
func (d *ComposerResolver) markAsQueued(p string) {
	d.queued.Add(p)
}

// shouldPackageBeQueued will return true if package p should be queued.
// False otherwise.
// A package should be queued if
// - it is not a system package
// - was not already queued
// - was not already resolved
func (d *ComposerResolver) shouldPackageBeQueued(p string) bool {
	if d.isSystemPackage(p) {
		return false
	}

	if d.isPackageAlreadyQueued(p) {
		return false
	}

	if d.isPackageAlreadyResolved(p) {
		return false
	}

	return true
}

// isPackageAlreadyResolved returns true if package p was already resolved.
// False otherwise.
func (d *ComposerResolver) isPackageAlreadyResolved(p string) bool {
	return d.resolved.Exists(p)
}

// isPackageAlreadyQueued returns true if package p was already queued.
// False otherwise.
func (d *ComposerResolver) isPackageAlreadyQueued(p string) bool {
	return d.queued.Exists(p)
}

// isSystemPackage returns true if p is a system package. False otherwise.
//
// A system package is a package that is not part of your package repository
// and and it needs to be fulfilled by the system.
// Examples: php, ext-curl
func (d *ComposerResolver) isSystemPackage(p string) bool {
	// If the package name don't contain a "/" we will skip it here.
	// In a composer.json in the require / require-dev part you normally add packaged
	// you depend on. A package name follows the format "vendor/package".
	// E.g. symfony/console
	// You can put other dependencies in here as well like `php` or `ext-zip`.
	// Those dependencies will be skipped (because they don`t have a vendor ;)).
	// The reason is simple: If you try to request the package "php" at packagist
	// you won`t get a JSON response with information we expect.
	// You will get valid HTML of the packagist search.
	// To avoid those errors and to save API calls we skip dependencies without a vendor.
	//
	// This follows the documentation as well:
	//
	// 	The package name consists of a vendor name and the project's name.
	// 	Often these will be identical - the vendor name just exists to prevent naming clashes.
	//	Source: https://getcomposer.org/doc/01-basic-usage.md
	return !strings.Contains(p, "/")
}
<commit_msg>Renamed channel "jobs" to "queue"<commit_after>package dependency

import (
	"fmt"
	"strings"
	"sync"

	"github.com/andygrunwald/perseus/dependency/repository"
	"github.com/andygrunwald/perseus/types/set"
)

// ComposerResolver is an implementation of Resolver for Composer (PHP)
type ComposerResolver struct {
	// repository is the Client to talk to a specific endpoint (e.g. Packagist)
	repository repository.Client

	// workerCount is the number of worker that will be started
	workerCount int
	waitGroup   sync.WaitGroup
	// queue is the channel where all jobs are stored that needs to be processed by the worker
	queue chan *Package
	// results is the channel where all resolved dependencies will be streamed
	results chan *Result
	// resolved is a storage to track which packages are already resolved
	resolved *set.Set
	// queued is a storage to track which packages were already queued
	queued *set.Set
	// replacee is a hashmap to replace old/renamed/obsolete packages that would throw an error otherwise
	replacee map[string]string
}

// GetResultStream will return the channel for results.
// During the process of resolving dependencies, this channel will be filled
// with the results. Those can be processed next to the resolve process.
func (d *ComposerResolver) GetResultStream() <-chan *Result {
	return d.results
}

// Resolve will start of the dependency resolver process.
func (d *ComposerResolver) Resolve(packageList []*Package) {
	d.startWorker()

	// Queue packages
	for _, p := range packageList {
		d.queuePackage(p)
	}

	// Wait until all packages are resolved and close everything
	d.waitGroup.Wait()
	close(d.queue)
	close(d.results)
}

// QueuePackage adds package p to the queue
func (d *ComposerResolver) queuePackage(p *Package) {
	d.waitGroup.Add(1)
	d.markAsQueued(p.Name)
	d.queue <- p
}

// startWorker will boot up the worker routines
func (d *ComposerResolver) startWorker() {
	for w := 1; w <= d.workerCount; w++ {
		go d.worker(w, d.queue, d.results)
	}
}

// worker is a single worker routine. This worker will be launched multiple times to work on
// the queue as efficient as possible.
// id is a unique number assigned per worker (only for logging/debugging purpose).
// jobs is the jobs channel. The worker needs to be able to add more jobs to the queue as well.
// results is the channel where all results will be stored once they are resolved.
func (d *ComposerResolver) worker(id int, queue chan<- *Package, results chan<- *Result) {
	// Worker has started. Lets do the hard work. Gimme the jobs.
	for j := range d.queue {
		packageName := j.Name

		// We don't need to process system packages.
		// System packages (like php or ext-curl) needs to be fulfilled by the system.
		// Not by the ApiClient
		if d.isSystemPackage(packageName) {
			d.waitGroup.Done()
			continue
		}

		// We overwrite specific packages, because they are added as dependencies to some older tags.
		// And those was renamed (for some reasons). But we are scanning all tags / branches.
		if r, ok := d.replacee[packageName]; ok {
			packageName = r
		}

		// Get information about the package from ApiClient
		p, resp, err := d.repository.GetPackageByName(packageName)
		if err != nil {
			// API Call error here. Request to Packagist failed
			r := &Result{
				Package: j,
				Error:   fmt.Errorf("API returned status code %d: %s", resp.StatusCode, err),
			}
			results <- r
			d.waitGroup.Done()
			continue
		}
		// Check if we got information from Packagist.
		// Maybe no error was thrown, but no package comes with the payload.
		// For us, as a dependency resolver, this is equal an error.
		if p == nil {
			// API Call error here. No package received from Packagist
			r := &Result{
				Package: j,
				Error:   fmt.Errorf("API Call to Packagist successful (Status code %d), but no package received", resp.StatusCode),
			}
			results <- r
			d.waitGroup.Done()
			continue
		}

		// Now we got the package.
		// Let us determine all requirements / dependencies from all versions,
		// because those packages needs to be resolved as well
		for _, version := range p.Versions {
			// If we don` have required packaged, we can handle the next one
			if len(version.Require) == 0 {
				continue
			}

			// Handle dependency per dependency
			for dependency := range version.Require {
				// We check if this dependency was already queued.
				// It is typical that many different versions of one package don't
				// change dependencies so often. So we would queue one package
				// multiple times. With this small check we save a lot of work here.
				if d.shouldPackageBeQueued(dependency) {
					d.markAsQueued(dependency)

					packageToResolve, _ := NewPackage(dependency, "")
					// We add two additional waitgroup entries here.
					// You might ask why? Regularly we add a new entry when we have a new package.
					// Here we add two, because of a) the new package and b) the new queue
					// entry of the package. We queue the package in a new go routine to
					// avoid a blocking state here. But we need to know when this go routine
					// is finished. So we observice this "Add package to queue" go routine
					// with the same waitgroup.
					d.waitGroup.Add(2)
					go func() {
						queue <- packageToResolve
						d.waitGroup.Done()
					}()
				}
			}
		}

		// Package was resolved. Lets do everything which is necessary to change this package to a result.
		resolvedPackage, err := NewPackage(p.Name, p.Repository)
		r := &Result{
			Package: resolvedPackage,
			Error:   err,
		}
		results <- r
		d.waitGroup.Done()
		d.markAsResolved(p.Name)
	}
}

// markAsResolved will mark package p as resolved.
func (d *ComposerResolver) markAsResolved(p string) {
	d.resolved.Add(p)
}

// markAsQueued will mark package p as queued.
func (d *ComposerResolver) markAsQueued(p string) {
	d.queued.Add(p)
}

// shouldPackageBeQueued will return true if package p should be queued.
// False otherwise.
// A package should be queued if
// - it is not a system package
// - was not already queued
// - was not already resolved
func (d *ComposerResolver) shouldPackageBeQueued(p string) bool {
	if d.isSystemPackage(p) {
		return false
	}

	if d.isPackageAlreadyQueued(p) {
		return false
	}

	if d.isPackageAlreadyResolved(p) {
		return false
	}

	return true
}

// isPackageAlreadyResolved returns true if package p was already resolved.
// False otherwise.
func (d *ComposerResolver) isPackageAlreadyResolved(p string) bool {
	return d.resolved.Exists(p)
}

// isPackageAlreadyQueued returns true if package p was already queued.
// False otherwise.
func (d *ComposerResolver) isPackageAlreadyQueued(p string) bool {
	return d.queued.Exists(p)
}

// isSystemPackage returns true if p is a system package. False otherwise.
//
// A system package is a package that is not part of your package repository
// and and it needs to be fulfilled by the system.
// Examples: php, ext-curl
func (d *ComposerResolver) isSystemPackage(p string) bool {
	// If the package name don't contain a "/" we will skip it here.
	// In a composer.json in the require / require-dev part you normally add packaged
	// you depend on. A package name follows the format "vendor/package".
	// E.g. symfony/console
	// You can put other dependencies in here as well like `php` or `ext-zip`.
	// Those dependencies will be skipped (because they don`t have a vendor ;)).
	// The reason is simple: If you try to request the package "php" at packagist
	// you won`t get a JSON response with information we expect.
	// You will get valid HTML of the packagist search.
	// To avoid those errors and to save API calls we skip dependencies without a vendor.
	//
	// This follows the documentation as well:
	//
	// 	The package name consists of a vendor name and the project's name.
	// 	Often these will be identical - the vendor name just exists to prevent naming clashes.
	//	Source: https://getcomposer.org/doc/01-basic-usage.md
	return !strings.Contains(p, "/")
}
<|endoftext|>


<commit_before>
package libkb

import (
)

type CmdPing struct {}

func (v CmdPing) Run() error {
	_, err := G.API.Get(ApiArg{ Endpoint : "ping" })
	return err
}

func (v CmdPing) UseConfig() bool { return true }
func (v CmdPing) UseKeyring() bool { return false }
func (v CmdPing) UseAPI() bool { return true }
<commit_msg>ping works!<commit_after>
package libkb

import (
	"fmt"
)

type CmdPing struct {}

func (v CmdPing) Run() error {
	_, err := G.API.Get(ApiArg{ Endpoint : "ping" })
	if err == nil {
		G.Log.Info(fmt.Sprintf("API Server at %s is up", G.Env.GetServerUri()))

	}
	return err
}

func (v CmdPing) UseConfig() bool { return true }
func (v CmdPing) UseKeyring() bool { return false }
func (v CmdPing) UseAPI() bool { return true }
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/BurntSushi/csql"

	"github.com/BurntSushi/goim/imdb"
)

var cmdSize = &command{
	name:      "size",
	shortHelp: "lists size of tables and total size of database",
	help:      "",
	flags:     flag.NewFlagSet("size", flag.ExitOnError),
	run:       size,
}

func size(c *command) bool {
	db := openDb(c.dbinfo())
	defer closeDb(db)

	tables, err := db.Tables()
	if err != nil {
		pef("%s", err)
		return false
	}
	tw := tabwriter.NewWriter(os.Stdout, 0, 2, 4, ' ', 0)
	for _, table := range tables {
		fmt.Fprintf(tw, "%s\t%s\n", table, tableSize(db, table))
	}

	_, dsn := c.dbinfo()
	total := databaseSize(db, dsn)
	fmt.Fprintf(tw, "total\t%s\n", total)
	tw.Flush()
	return true
}

// tableSize returns a pretty string indicating the size in table. Row count
// is always include, but the size on disk is only included if it's supported
// by the database.
// Note that 'name' is assumed to be SQL-safe.
func tableSize(db *imdb.DB, name string) string {
	count := csql.Count(db, sf("SELECT COUNT(*) AS count FROM %s", name))
	if db.Driver == "sqlite3" {
		return sf("%d rows", count)
	}
	var size string
	q := sf("SELECT pg_size_pretty(pg_relation_size('%s'))", name)
	csql.Scan(db.QueryRow(q), &size)
	return sf("%d rows (%s)", count, size)
}

// databaseSize returns a pretty string indicating the size of the entire
// database on disk.
func databaseSize(db *imdb.DB, dsn string) string {
	if db.Driver == "sqlite3" {
		fi, err := os.Stat(dsn)
		csql.Panic(err)
		return prettyFileSize(fi.Size())
	}
	var size string
	q := sf("SELECT pg_size_pretty(pg_database_size(current_database()))")
	csql.Scan(db.QueryRow(q), &size)
	return size
}

func prettyFileSize(bytes int64) string {
	cutoff := int64(1024 * 2)
	kb, mb, gb := int64(1024), int64(1024*1024), int64(1024*1024*1024)
	if bytes < cutoff {
		return sf("%d bytes", bytes)
	}
	kbytes := bytes / kb
	if kbytes < cutoff {
		return sf("%d kB", kbytes)
	}
	mbytes := bytes / mb
	if mbytes < cutoff {
		return sf("%d MB", mbytes)
	}
	return sf("%d GB", bytes/gb)
}
<commit_msg>Make name of 'size' command function consistent.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/BurntSushi/csql"

	"github.com/BurntSushi/goim/imdb"
)

var cmdSize = &command{
	name:      "size",
	shortHelp: "lists size of tables and total size of database",
	help:      "",
	flags:     flag.NewFlagSet("size", flag.ExitOnError),
	run:       cmd_size,
}

func cmd_size(c *command) bool {
	db := openDb(c.dbinfo())
	defer closeDb(db)

	tables, err := db.Tables()
	if err != nil {
		pef("%s", err)
		return false
	}
	tw := tabwriter.NewWriter(os.Stdout, 0, 2, 4, ' ', 0)
	for _, table := range tables {
		fmt.Fprintf(tw, "%s\t%s\n", table, tableSize(db, table))
	}

	_, dsn := c.dbinfo()
	total := databaseSize(db, dsn)
	fmt.Fprintf(tw, "total\t%s\n", total)
	tw.Flush()
	return true
}

// tableSize returns a pretty string indicating the size in table. Row count
// is always include, but the size on disk is only included if it's supported
// by the database.
// Note that 'name' is assumed to be SQL-safe.
func tableSize(db *imdb.DB, name string) string {
	count := csql.Count(db, sf("SELECT COUNT(*) AS count FROM %s", name))
	if db.Driver == "sqlite3" {
		return sf("%d rows", count)
	}
	var size string
	q := sf("SELECT pg_size_pretty(pg_relation_size('%s'))", name)
	csql.Scan(db.QueryRow(q), &size)
	return sf("%d rows (%s)", count, size)
}

// databaseSize returns a pretty string indicating the size of the entire
// database on disk.
func databaseSize(db *imdb.DB, dsn string) string {
	if db.Driver == "sqlite3" {
		fi, err := os.Stat(dsn)
		csql.Panic(err)
		return prettyFileSize(fi.Size())
	}
	var size string
	q := sf("SELECT pg_size_pretty(pg_database_size(current_database()))")
	csql.Scan(db.QueryRow(q), &size)
	return size
}

func prettyFileSize(bytes int64) string {
	cutoff := int64(1024 * 2)
	kb, mb, gb := int64(1024), int64(1024*1024), int64(1024*1024*1024)
	if bytes < cutoff {
		return sf("%d bytes", bytes)
	}
	kbytes := bytes / kb
	if kbytes < cutoff {
		return sf("%d kB", kbytes)
	}
	mbytes := bytes / mb
	if mbytes < cutoff {
		return sf("%d MB", mbytes)
	}
	return sf("%d GB", bytes/gb)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

type benchHandler struct {
	mutex sync.RWMutex
	conns map[*websocket.Conn]*connection
}

type connection struct {
	sync.Mutex
	ws *websocket.Conn
}

type WsMsg struct {
	Type    string      `json:"type"`
	Payload interface{} `json:"payload"`
}

type BroadcastResult struct {
	Type          string      `json:"type"`
	Payload       interface{} `json:"payload"`
	ListenerCount int         `json:"listenerCount"`
}

func NewBenchHandler() *benchHandler {
	return &benchHandler{
		conns: make(map[*websocket.Conn]*connection),
	}
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  256,
	WriteBufferSize: 256,
}

func (h *benchHandler) ServeHTTP(rsp http.ResponseWriter, req *http.Request) {
	conn, err := upgrader.Upgrade(rsp, req, nil)
	if err != nil {
		log.Println(err)
		return
	}
	h.Accept(conn)
}

func (h *benchHandler) Accept(ws *websocket.Conn) {
	defer h.cleanup(ws)

	c := &connection{ws: ws}

	h.mutex.Lock()
	h.conns[ws] = c
	h.mutex.Unlock()

	for {
		var msg WsMsg
		err := websocket.ReadJSON(ws, &msg)
		if err != nil && err.(*websocket.CloseError) != nil && err.(*websocket.CloseError).Code == websocket.CloseAbnormalClosure {
			return
		} else if err != nil {
			log.Println("websocket.ReadJSON err:", err)
			return
		}

		switch msg.Type {
		case "echo":
			if err := h.echo(c, msg.Payload); err != nil {
				log.Println("echo err:", err)
				return
			}
		case "broadcast":
			if err := h.broadcast(c, msg.Payload); err != nil {
				log.Println("broadcast err:", err)
				return
			}
		default:
			log.Println("unknown msg.Type")
			return
		}
	}
}

func (h *benchHandler) echo(c *connection, payload interface{}) error {
	c.Lock()
	defer c.Unlock()
	return c.ws.WriteJSON(&WsMsg{Type: "echo", Payload: payload})
}

func (h *benchHandler) broadcast(c *connection, payload interface{}) error {
	result := BroadcastResult{Type: "broadcastResult", Payload: payload}

	msg, err := json.Marshal(&WsMsg{Type: "broadcast", Payload: payload})

	if err != nil {
		log.Println("broadcast json marshal err:", err)
	} else {

		h.mutex.RLock()

		for _, c := range h.conns {
			c.Lock()
			if err := c.ws.WriteMessage(websocket.TextMessage, msg); err == nil {
				result.ListenerCount++
			}
			c.Unlock()
		}
		h.mutex.RUnlock()
	}

	c.Lock()
	defer c.Unlock()
	return c.ws.WriteJSON(&result)
}

func (h *benchHandler) cleanup(ws *websocket.Conn) {
	ws.Close()
	h.mutex.Lock()

	delete(h.conns, ws)

	h.mutex.Unlock()
}
<commit_msg>Test type assertion to avoid panic<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

type benchHandler struct {
	mutex sync.RWMutex
	conns map[*websocket.Conn]*connection
}

type connection struct {
	sync.Mutex
	ws *websocket.Conn
}

type WsMsg struct {
	Type    string      `json:"type"`
	Payload interface{} `json:"payload"`
}

type BroadcastResult struct {
	Type          string      `json:"type"`
	Payload       interface{} `json:"payload"`
	ListenerCount int         `json:"listenerCount"`
}

func NewBenchHandler() *benchHandler {
	return &benchHandler{
		conns: make(map[*websocket.Conn]*connection),
	}
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  256,
	WriteBufferSize: 256,
}

func (h *benchHandler) ServeHTTP(rsp http.ResponseWriter, req *http.Request) {
	conn, err := upgrader.Upgrade(rsp, req, nil)
	if err != nil {
		log.Println(err)
		return
	}
	h.Accept(conn)
}

func (h *benchHandler) Accept(ws *websocket.Conn) {
	defer h.cleanup(ws)

	c := &connection{ws: ws}

	h.mutex.Lock()
	h.conns[ws] = c
	h.mutex.Unlock()

	for {
		var msg WsMsg
		err := websocket.ReadJSON(ws, &msg)
		if err != nil {
			if err, ok := err.(*websocket.CloseError); ok && err.Code == websocket.CloseAbnormalClosure {
				return
			}
			log.Println("websocket.ReadJSON err:", err)
			return
		}

		switch msg.Type {
		case "echo":
			if err := h.echo(c, msg.Payload); err != nil {
				log.Println("echo err:", err)
				return
			}
		case "broadcast":
			if err := h.broadcast(c, msg.Payload); err != nil {
				log.Println("broadcast err:", err)
				return
			}
		default:
			log.Println("unknown msg.Type")
			return
		}
	}
}

func (h *benchHandler) echo(c *connection, payload interface{}) error {
	c.Lock()
	defer c.Unlock()
	return c.ws.WriteJSON(&WsMsg{Type: "echo", Payload: payload})
}

func (h *benchHandler) broadcast(c *connection, payload interface{}) error {
	result := BroadcastResult{Type: "broadcastResult", Payload: payload}

	msg, err := json.Marshal(&WsMsg{Type: "broadcast", Payload: payload})

	if err != nil {
		log.Println("broadcast json marshal err:", err)
	} else {

		h.mutex.RLock()

		for _, c := range h.conns {
			c.Lock()
			if err := c.ws.WriteMessage(websocket.TextMessage, msg); err == nil {
				result.ListenerCount++
			}
			c.Unlock()
		}
		h.mutex.RUnlock()
	}

	c.Lock()
	defer c.Unlock()
	return c.ws.WriteJSON(&result)
}

func (h *benchHandler) cleanup(ws *websocket.Conn) {
	ws.Close()
	h.mutex.Lock()

	delete(h.conns, ws)

	h.mutex.Unlock()
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"strings"
	"testing"

	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestValidateCloudIoTID(t *testing.T) {
	x := []StringValidationTestCase{
		// No errors
		{TestName: "basic", Value: "foobar"},
		{TestName: "with numbers", Value: "foobar123"},
		{TestName: "short", Value: "foo"},
		{TestName: "long", Value: "foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoo"},
		{TestName: "has a hyphen", Value: "foo-bar"},

		// With errors
		{TestName: "empty", Value: "", ExpectError: true},
		{TestName: "starts with a goog", Value: "googfoobar", ExpectError: true},
		{TestName: "starts with a number", Value: "1foobar", ExpectError: true},
		{TestName: "has an slash", Value: "foo/bar", ExpectError: true},
		{TestName: "has an backslash", Value: "foo\bar", ExpectError: true},
		{TestName: "too long", Value: strings.Repeat("f", 260), ExpectError: true},
	}

	es := testStringValidationCases(x, validateCloudIotID)
	if len(es) > 0 {
		t.Errorf("Failed to validate CloudIoT ID names: %v", es)
	}
}

func TestAccCloudIoTRegistry_basic(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_basic(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_extended(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_extended(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_update(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_basic(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccCloudIoTRegistry_extended(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccCloudIoTRegistry_basic(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_eventNotificationConfigsSingle(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))
	topic := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_singleEventNotificationConfigs(topic, registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_eventNotificationConfigsMultiple(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))
	topic := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_multipleEventNotificationConfigs(topic, registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckCloudIoTRegistryDestroy(s *terraform.State) error {
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "google_cloudiot_registry" {
			continue
		}
		config := testAccProvider.Meta().(*Config)
		registry, _ := config.clientCloudIoT.Projects.Locations.Registries.Get(rs.Primary.ID).Do()
		if registry != nil {
			return fmt.Errorf("Registry still present")
		}
	}
	return nil
}

func testAccCloudIoTRegistry_basic(registryName string) string {
	return fmt.Sprintf(`
resource "google_cloudiot_registry" "foobar" {
  name = "%s"
}
`, registryName)
}

func testAccCloudIoTRegistry_extended(registryName string) string {
	return fmt.Sprintf(`
resource "google_project_iam_binding" "cloud-iot-iam-binding" {
  members = ["serviceAccount:cloud-iot@system.gserviceaccount.com"]
  role    = "roles/pubsub.publisher"
}

resource "google_pubsub_topic" "default-devicestatus" {
  name = "psregistry-test-devicestatus-%s"
}

resource "google_pubsub_topic" "default-telemetry" {
  name = "psregistry-test-telemetry-%s"
}

resource "google_cloudiot_registry" "foobar" {
  depends_on = [google_project_iam_binding.cloud-iot-iam-binding]

  name = "%s"

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.default-devicestatus.id
  }

  state_notification_config = {
    pubsub_topic_name = google_pubsub_topic.default-telemetry.id
  }

  http_config = {
    http_enabled_state = "HTTP_DISABLED"
  }

  mqtt_config = {
    mqtt_enabled_state = "MQTT_DISABLED"
  }

  log_level = "INFO"

  credentials {
    public_key_certificate = {
      format      = "X509_CERTIFICATE_PEM"
      certificate = file("test-fixtures/rsa_cert.pem")
    }
  }
}
`, acctest.RandString(10), acctest.RandString(10), registryName)
}

func testAccCloudIoTRegistry_singleEventNotificationConfigs(topic, registryName string) string {
	return fmt.Sprintf(`
resource "google_project_iam_binding" "cloud-iot-iam-binding" {
  members = ["serviceAccount:cloud-iot@system.gserviceaccount.com"]
  role    = "roles/pubsub.publisher"
}

resource "google_pubsub_topic" "event-topic-1" {
  name = "%s"
}

resource "google_cloudiot_registry" "foobar" {
  depends_on = [google_project_iam_binding.cloud-iot-iam-binding]

  name = "%s"

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.event-topic-1.id
    subfolder_matches = ""
  }
}
`, topic, registryName)
}

func testAccCloudIoTRegistry_multipleEventNotificationConfigs(topic, registryName string) string {
	return fmt.Sprintf(`
resource "google_project_iam_binding" "cloud-iot-iam-binding" {
  members = ["serviceAccount:cloud-iot@system.gserviceaccount.com"]
  role    = "roles/pubsub.publisher"
}

resource "google_pubsub_topic" "event-topic-1" {
  name = "%s"
}

resource "google_pubsub_topic" "event-topic-2" {
  name = "%s-alt"
}

resource "google_cloudiot_registry" "foobar" {
  depends_on = [google_project_iam_binding.cloud-iot-iam-binding]

  name = "%s"

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.event-topic-1.id
    subfolder_matches = "test"
  }

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.event-topic-2.id
    subfolder_matches = ""
  }
}
`, topic, topic, registryName)
}
<commit_msg>Remove iam bindings from cloudIoT registry tests (#1642)<commit_after>package google

import (
	"fmt"
	"strings"
	"testing"

	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestValidateCloudIoTID(t *testing.T) {
	x := []StringValidationTestCase{
		// No errors
		{TestName: "basic", Value: "foobar"},
		{TestName: "with numbers", Value: "foobar123"},
		{TestName: "short", Value: "foo"},
		{TestName: "long", Value: "foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoo"},
		{TestName: "has a hyphen", Value: "foo-bar"},

		// With errors
		{TestName: "empty", Value: "", ExpectError: true},
		{TestName: "starts with a goog", Value: "googfoobar", ExpectError: true},
		{TestName: "starts with a number", Value: "1foobar", ExpectError: true},
		{TestName: "has an slash", Value: "foo/bar", ExpectError: true},
		{TestName: "has an backslash", Value: "foo\bar", ExpectError: true},
		{TestName: "too long", Value: strings.Repeat("f", 260), ExpectError: true},
	}

	es := testStringValidationCases(x, validateCloudIotID)
	if len(es) > 0 {
		t.Errorf("Failed to validate CloudIoT ID names: %v", es)
	}
}

func TestAccCloudIoTRegistry_basic(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_basic(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_extended(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_extended(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_update(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_basic(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccCloudIoTRegistry_extended(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccCloudIoTRegistry_basic(registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_eventNotificationConfigsSingle(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))
	topic := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_singleEventNotificationConfigs(topic, registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccCloudIoTRegistry_eventNotificationConfigsMultiple(t *testing.T) {
	t.Parallel()

	registryName := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))
	topic := fmt.Sprintf("tf-registry-test-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudIoTRegistryDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCloudIoTRegistry_multipleEventNotificationConfigs(topic, registryName),
			},
			{
				ResourceName:      "google_cloudiot_registry.foobar",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckCloudIoTRegistryDestroy(s *terraform.State) error {
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "google_cloudiot_registry" {
			continue
		}
		config := testAccProvider.Meta().(*Config)
		registry, _ := config.clientCloudIoT.Projects.Locations.Registries.Get(rs.Primary.ID).Do()
		if registry != nil {
			return fmt.Errorf("Registry still present")
		}
	}
	return nil
}

func testAccCloudIoTRegistry_basic(registryName string) string {
	return fmt.Sprintf(`
resource "google_cloudiot_registry" "foobar" {
  name = "%s"
}
`, registryName)
}

func testAccCloudIoTRegistry_extended(registryName string) string {
	return fmt.Sprintf(`
resource "google_pubsub_topic" "default-devicestatus" {
  name = "psregistry-test-devicestatus-%s"
}

resource "google_pubsub_topic" "default-telemetry" {
  name = "psregistry-test-telemetry-%s"
}

resource "google_cloudiot_registry" "foobar" {
  name = "%s"

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.default-devicestatus.id
  }

  state_notification_config = {
    pubsub_topic_name = google_pubsub_topic.default-telemetry.id
  }

  http_config = {
    http_enabled_state = "HTTP_DISABLED"
  }

  mqtt_config = {
    mqtt_enabled_state = "MQTT_DISABLED"
  }

  log_level = "INFO"

  credentials {
    public_key_certificate = {
      format      = "X509_CERTIFICATE_PEM"
      certificate = file("test-fixtures/rsa_cert.pem")
    }
  }
}
`, acctest.RandString(10), acctest.RandString(10), registryName)
}

func testAccCloudIoTRegistry_singleEventNotificationConfigs(topic, registryName string) string {
	return fmt.Sprintf(`
resource "google_pubsub_topic" "event-topic-1" {
  name = "%s"
}

resource "google_cloudiot_registry" "foobar" {
  name = "%s"

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.event-topic-1.id
    subfolder_matches = ""
  }
}
`, topic, registryName)
}

func testAccCloudIoTRegistry_multipleEventNotificationConfigs(topic, registryName string) string {
	return fmt.Sprintf(`
resource "google_pubsub_topic" "event-topic-1" {
  name = "%s"
}

resource "google_pubsub_topic" "event-topic-2" {
  name = "%s-alt"
}

resource "google_cloudiot_registry" "foobar" {
  name = "%s"

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.event-topic-1.id
    subfolder_matches = "test"
  }

  event_notification_configs {
    pubsub_topic_name = google_pubsub_topic.event-topic-2.id
    subfolder_matches = ""
  }
}
`, topic, topic, registryName)
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"sort"
	"testing"

	"github.com/keybase/client/go/protocol"
	"github.com/keybase/go-codec/codec"
	"github.com/stretchr/testify/require"
)

// Test that GetTlfHandle() generates a TlfHandle properly for public
// TLFs if there is no cached TlfHandle.
func TestRootMetadataGetTlfHandlePublic(t *testing.T) {
	tlfID := FakeTlfID(0, true)
	rmd := NewRootMetadata(nil, tlfID)
	dirHandle := rmd.GetTlfHandle()
	if dirHandle == nil {
		t.Fatal("nil TlfHandle")
	}
	if len(dirHandle.Readers) != 1 || dirHandle.Readers[0] != keybase1.PublicUID {
		t.Errorf("Invalid reader list %v", dirHandle.Readers)
	}
	if len(dirHandle.Writers) != 0 {
		t.Errorf("Invalid writer list %v", dirHandle.Writers)
	}
}

// Test that GetTlfHandle() generates a TlfHandle properly for
// non-public TLFs if there is no cached TlfHandle.
func TestRootMetadataGetTlfHandlePrivate(t *testing.T) {
	tlfID := FakeTlfID(0, false)
	rmd := NewRootMetadata(nil, tlfID)
	AddNewKeysOrBust(t, rmd, *NewTLFKeyBundle())
	dirHandle := rmd.GetTlfHandle()
	if dirHandle == nil {
		t.Fatal("nil TlfHandle")
	}
	if len(dirHandle.Readers) != 0 {
		t.Errorf("Invalid reader list %v", dirHandle.Readers)
	}
	if len(dirHandle.Writers) != 0 {
		t.Errorf("Invalid writer list %v", dirHandle.Writers)
	}
}

// Test that key generations work as expected for private TLFs.
func TestRootMetadataLatestKeyGenerationPrivate(t *testing.T) {
	tlfID := FakeTlfID(0, false)
	rmd := NewRootMetadata(nil, tlfID)
	if rmd.LatestKeyGeneration() != 0 {
		t.Errorf("Expected key generation to be invalid (0)")
	}
	AddNewKeysOrBust(t, rmd, *NewTLFKeyBundle())
	if rmd.LatestKeyGeneration() != FirstValidKeyGen {
		t.Errorf("Expected key generation to be valid(%d)", FirstValidKeyGen)
	}
}

// Test that key generations work as expected for public TLFs.
func TestRootMetadataLatestKeyGenerationPublic(t *testing.T) {
	tlfID := FakeTlfID(0, true)
	rmd := NewRootMetadata(nil, tlfID)
	if rmd.LatestKeyGeneration() != PublicKeyGen {
		t.Errorf("Expected key generation to be public (%d)", PublicKeyGen)
	}
}

// Test that old encoded WriterMetadata objects (i.e., without any
// extra fields) can be deserialized and serialized to the same form,
// which is important for RootMetadata.VerifyWriterMetadata().
func TestWriterMetadataUnchangedEncoding(t *testing.T) {
	encodedWm := []byte{
		0x89, 0xa3, 0x42, 0x49, 0x44, 0xc4, 0x10, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa9,
		0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67,
		0x65, 0x64, 0xa2, 0x49, 0x44, 0xc4, 0x10, 0x1,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xb3,
		0x4c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69,
		0x66, 0x79, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69,
		0x74, 0x65, 0x72, 0xa4, 0x75, 0x69, 0x64, 0x31,
		0xa8, 0x52, 0x65, 0x66, 0x42, 0x79, 0x74, 0x65,
		0x73, 0x63, 0xaa, 0x55, 0x6e, 0x72, 0x65, 0x66,
		0x42, 0x79, 0x74, 0x65, 0x73, 0x65, 0xa6, 0x57,
		0x46, 0x6c, 0x61, 0x67, 0x73, 0xa, 0xa7, 0x57,
		0x72, 0x69, 0x74, 0x65, 0x72, 0x73, 0x92, 0xa4,
		0x75, 0x69, 0x64, 0x31, 0xa4, 0x75, 0x69, 0x64,
		0x32, 0xa4, 0x64, 0x61, 0x74, 0x61, 0xc4, 0x2,
		0xa, 0xb,
	}

	expectedWm := WriterMetadata{
		SerializedPrivateMetadata: []byte{0xa, 0xb},
		LastModifyingWriter:       "uid1",
		Writers:                   []keybase1.UID{"uid1", "uid2"},
		ID:                        FakeTlfID(1, false),
		BID:                       NullBranchID,
		WFlags:                    0xa,
		DiskUsage:                 100,
		RefBytes:                  99,
		UnrefBytes:                101,
	}

	c := NewCodecMsgpack()

	var wm WriterMetadata
	err := c.Decode(encodedWm, &wm)
	require.Nil(t, err)

	require.Equal(t, expectedWm, wm)

	buf, err := c.Encode(wm)
	require.Nil(t, err)
	require.Equal(t, encodedWm, buf)
}

// Test that WriterMetadata has only a fixed (frozen) set of fields.
func TestWriterMetadataEncodedFields(t *testing.T) {
	// Usually exactly one of Writers/WKeys is filled in, but we
	// fill in both here for testing.
	wm := WriterMetadata{
		Writers: []keybase1.UID{"uid1", "uid2"},
		WKeys:   TLFWriterKeyGenerations{nil},
		Extra:   &WriterMetadataExtra{},
	}

	c := NewCodecMsgpack()

	buf, err := c.Encode(wm)
	require.Nil(t, err)

	var m map[string]interface{}
	err = c.Decode(buf, &m)
	require.Nil(t, err)

	expectedFields := []string{
		"BID",
		"DiskUsage",
		"ID",
		"LastModifyingWriter",
		"RefBytes",
		"UnrefBytes",
		"WFlags",
		"WKeys",
		"Writers",
		"data",
		"x",
	}

	var fields []string
	for field := range m {
		fields = append(fields, field)
	}
	sort.Strings(fields)
	require.Equal(t, expectedFields, fields)
}

type writerMetadataCurrent WriterMetadata

func (wm writerMetadataCurrent) deepCopyStruct(f copyFields) currentStruct {
	return writerMetadataCurrent(WriterMetadata(wm).deepCopyHelper(f))
}

type writerMetadataExtraFuture struct {
	WriterMetadataExtra
	extra
}

func (wmef writerMetadataExtraFuture) toCurrent() WriterMetadataExtra {
	return wmef.WriterMetadataExtra
}

type tlfWriterKeyGenerationsFuture []*tlfWriterKeyBundleFuture

func (wkgf tlfWriterKeyGenerationsFuture) toCurrent() TLFWriterKeyGenerations {
	wkg := make(TLFWriterKeyGenerations, len(wkgf))
	for i, wkbf := range wkgf {
		wkb := wkbf.toCurrent()
		wkg[i] = (*TLFWriterKeyBundle)(&wkb)
	}
	return wkg
}

type writerMetadataFuture struct {
	writerMetadataCurrent
	// Override WriterMetadata.WKeys.
	WKeys tlfWriterKeyGenerationsFuture
	// Override WriterMetadata.Extra.
	Extra *writerMetadataExtraFuture `codec:"x,omitempty"`
}

func (wmf writerMetadataFuture) toCurrent() writerMetadataCurrent {
	wm := wmf.writerMetadataCurrent
	wm.WKeys = wmf.WKeys.toCurrent()
	// Maintain the invariant that Extra is either non-empty or
	// nil.
	//
	// TODO: Once we add fields to Extra, we'll have to change
	// this to:
	//
	// if wmf.Extra != nil {
	//   x := wmf.Extra.toCurrent()
	//   wm.Extra = &x
	// }
	wm.Extra = nil
	return wm
}

func (wmf writerMetadataFuture) toCurrentStruct() currentStruct {
	return wmf.toCurrent()
}

func makeFakeWriterMetadataFuture(t *testing.T) writerMetadataFuture {
	wmd := writerMetadataCurrent{
		[]byte{0xa, 0xb},
		"uid1",
		[]keybase1.UID{"uid1", "uid2"},
		nil,
		FakeTlfID(1, false),
		NullBranchID,
		0xa,
		100,
		99,
		101,
		nil,
	}
	wkb := makeFakeTLFWriterKeyBundleFuture(t)
	return writerMetadataFuture{
		wmd,
		tlfWriterKeyGenerationsFuture{&wkb},
		&writerMetadataExtraFuture{
			WriterMetadataExtra{
				codec.UnknownFieldSet{},
			},
			makeExtraOrBust("WriterMetadata", t),
		},
	}
}

func TestWriterMetadataUnknownFields(t *testing.T) {
	testStructUnknownFields(t, makeFakeWriterMetadataFuture(t))
}

type rootMetadataCurrent RootMetadata

func (rm rootMetadataCurrent) deepCopyStruct(f copyFields) currentStruct {
	return rootMetadataCurrent(RootMetadata(rm).deepCopyHelper(f))
}

type tlfReaderKeyGenerationsFuture []*tlfReaderKeyBundleFuture

func (rkgf tlfReaderKeyGenerationsFuture) toCurrent() TLFReaderKeyGenerations {
	rkg := make(TLFReaderKeyGenerations, len(rkgf))
	for i, rkbf := range rkgf {
		rkb := rkbf.toCurrent()
		rkg[i] = (*TLFReaderKeyBundle)(&rkb)
	}
	return rkg
}

// rootMetadataCurrentWrapper exists only to add extra depth to fields
// in rootMetadataCurrent, so that they may be overridden in
// rootMetadataFuture.
type rootMetadataCurrentWrapper struct {
	rootMetadataCurrent
}

type rootMetadataFuture struct {
	// Override RootMetadata.WriterMetadata. Put it first to work
	// around a bug in codec's field lookup code.
	//
	// TODO: Report and fix this bug upstream.
	writerMetadataFuture

	rootMetadataCurrentWrapper
	// Override RootMetadata.RKeys.
	RKeys tlfReaderKeyGenerationsFuture `codec:",omitempty"`
	extra
}

func (rmf rootMetadataFuture) toCurrent() rootMetadataCurrent {
	rm := rmf.rootMetadataCurrentWrapper.rootMetadataCurrent
	rm.WriterMetadata = WriterMetadata(rmf.writerMetadataFuture.toCurrent())
	rm.RKeys = rmf.RKeys.toCurrent()
	return rm
}

func (rmf rootMetadataFuture) toCurrentStruct() currentStruct {
	return rmf.toCurrent()
}

func makeFakeRootMetadataFuture(t *testing.T) rootMetadataFuture {
	wmf := makeFakeWriterMetadataFuture(t)
	rkb := makeFakeTLFReaderKeyBundleFuture(t)
	h, err := DefaultHash([]byte("fake buf"))
	require.Nil(t, err)
	rmf := rootMetadataFuture{
		wmf,
		rootMetadataCurrentWrapper{
			rootMetadataCurrent{
				WriterMetadata{},
				SignatureInfo{
					100,
					[]byte{0xc},
					MakeFakeVerifyingKeyOrBust("fake kid"),
				},
				"uid1",
				0xb,
				5,
				MdID{h},
				nil,
				codec.UnknownFieldSet{},
				PrivateMetadata{},
				nil,
				MdID{},
				false,
			},
		},
		[]*tlfReaderKeyBundleFuture{&rkb},
		makeExtraOrBust("RootMetadata", t),
	}
	_ = rmf.ID
	return rmf
}

func TestRootMetadataUnknownFields(t *testing.T) {
	testStructUnknownFields(t, makeFakeRootMetadataFuture(t))
}
<commit_msg>Fixed RMD tests<commit_after>package libkbfs

import (
	"sort"
	"testing"

	"github.com/keybase/client/go/protocol"
	"github.com/keybase/go-codec/codec"
	"github.com/stretchr/testify/require"
)

// Test that GetTlfHandle() generates a TlfHandle properly for public
// TLFs if there is no cached TlfHandle.
func TestRootMetadataGetTlfHandlePublic(t *testing.T) {
	tlfID := FakeTlfID(0, true)
	rmd := NewRootMetadata(nil, tlfID)
	dirHandle := rmd.GetTlfHandle()
	if dirHandle == nil {
		t.Fatal("nil TlfHandle")
	}
	if len(dirHandle.Readers) != 1 || dirHandle.Readers[0] != keybase1.PublicUID {
		t.Errorf("Invalid reader list %v", dirHandle.Readers)
	}
	if len(dirHandle.Writers) != 0 {
		t.Errorf("Invalid writer list %v", dirHandle.Writers)
	}
}

// Test that GetTlfHandle() generates a TlfHandle properly for
// non-public TLFs if there is no cached TlfHandle.
func TestRootMetadataGetTlfHandlePrivate(t *testing.T) {
	tlfID := FakeTlfID(0, false)
	rmd := NewRootMetadata(nil, tlfID)
	AddNewKeysOrBust(t, rmd, *NewTLFKeyBundle())
	dirHandle := rmd.GetTlfHandle()
	if dirHandle == nil {
		t.Fatal("nil TlfHandle")
	}
	if len(dirHandle.Readers) != 0 {
		t.Errorf("Invalid reader list %v", dirHandle.Readers)
	}
	if len(dirHandle.Writers) != 0 {
		t.Errorf("Invalid writer list %v", dirHandle.Writers)
	}
}

// Test that key generations work as expected for private TLFs.
func TestRootMetadataLatestKeyGenerationPrivate(t *testing.T) {
	tlfID := FakeTlfID(0, false)
	rmd := NewRootMetadata(nil, tlfID)
	if rmd.LatestKeyGeneration() != 0 {
		t.Errorf("Expected key generation to be invalid (0)")
	}
	AddNewKeysOrBust(t, rmd, *NewTLFKeyBundle())
	if rmd.LatestKeyGeneration() != FirstValidKeyGen {
		t.Errorf("Expected key generation to be valid(%d)", FirstValidKeyGen)
	}
}

// Test that key generations work as expected for public TLFs.
func TestRootMetadataLatestKeyGenerationPublic(t *testing.T) {
	tlfID := FakeTlfID(0, true)
	rmd := NewRootMetadata(nil, tlfID)
	if rmd.LatestKeyGeneration() != PublicKeyGen {
		t.Errorf("Expected key generation to be public (%d)", PublicKeyGen)
	}
}

// Test that old encoded WriterMetadata objects (i.e., without any
// extra fields) can be deserialized and serialized to the same form,
// which is important for RootMetadata.VerifyWriterMetadata().
func TestWriterMetadataUnchangedEncoding(t *testing.T) {
	encodedWm := []byte{
		0x89, 0xa3, 0x42, 0x49, 0x44, 0xc4, 0x10, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa9,
		0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67,
		0x65, 0x64, 0xa2, 0x49, 0x44, 0xc4, 0x10, 0x1,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xb3,
		0x4c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69,
		0x66, 0x79, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69,
		0x74, 0x65, 0x72, 0xa4, 0x75, 0x69, 0x64, 0x31,
		0xa8, 0x52, 0x65, 0x66, 0x42, 0x79, 0x74, 0x65,
		0x73, 0x63, 0xaa, 0x55, 0x6e, 0x72, 0x65, 0x66,
		0x42, 0x79, 0x74, 0x65, 0x73, 0x65, 0xa6, 0x57,
		0x46, 0x6c, 0x61, 0x67, 0x73, 0xa, 0xa7, 0x57,
		0x72, 0x69, 0x74, 0x65, 0x72, 0x73, 0x92, 0xa4,
		0x75, 0x69, 0x64, 0x31, 0xa4, 0x75, 0x69, 0x64,
		0x32, 0xa4, 0x64, 0x61, 0x74, 0x61, 0xc4, 0x2,
		0xa, 0xb,
	}

	expectedWm := WriterMetadata{
		SerializedPrivateMetadata: []byte{0xa, 0xb},
		LastModifyingWriter:       "uid1",
		Writers:                   []keybase1.UID{"uid1", "uid2"},
		ID:                        FakeTlfID(1, false),
		BID:                       NullBranchID,
		WFlags:                    0xa,
		DiskUsage:                 100,
		RefBytes:                  99,
		UnrefBytes:                101,
	}

	c := NewCodecMsgpack()

	var wm WriterMetadata
	err := c.Decode(encodedWm, &wm)
	require.Nil(t, err)

	require.Equal(t, expectedWm, wm)

	buf, err := c.Encode(wm)
	require.Nil(t, err)
	require.Equal(t, encodedWm, buf)
}

// Test that WriterMetadata has only a fixed (frozen) set of fields.
func TestWriterMetadataEncodedFields(t *testing.T) {
	// Usually exactly one of Writers/WKeys is filled in, but we
	// fill in both here for testing.
	wm := WriterMetadata{
		Writers: []keybase1.UID{"uid1", "uid2"},
		WKeys:   TLFWriterKeyGenerations{nil},
		Extra:   &WriterMetadataExtra{},
	}

	c := NewCodecMsgpack()

	buf, err := c.Encode(wm)
	require.Nil(t, err)

	var m map[string]interface{}
	err = c.Decode(buf, &m)
	require.Nil(t, err)

	expectedFields := []string{
		"BID",
		"DiskUsage",
		"ID",
		"LastModifyingWriter",
		"RefBytes",
		"UnrefBytes",
		"WFlags",
		"WKeys",
		"Writers",
		"data",
		"x",
	}

	var fields []string
	for field := range m {
		fields = append(fields, field)
	}
	sort.Strings(fields)
	require.Equal(t, expectedFields, fields)
}

type writerMetadataCurrent WriterMetadata

func (wm writerMetadataCurrent) deepCopyStruct(f copyFields) currentStruct {
	return writerMetadataCurrent(WriterMetadata(wm).deepCopyHelper(f))
}

type writerMetadataExtraFuture struct {
	WriterMetadataExtra
	extra
}

func (wmef writerMetadataExtraFuture) toCurrent() WriterMetadataExtra {
	return wmef.WriterMetadataExtra
}

type tlfWriterKeyGenerationsFuture []*tlfWriterKeyBundleFuture

func (wkgf tlfWriterKeyGenerationsFuture) toCurrent() TLFWriterKeyGenerations {
	wkg := make(TLFWriterKeyGenerations, len(wkgf))
	for i, wkbf := range wkgf {
		wkb := wkbf.toCurrent()
		wkg[i] = (*TLFWriterKeyBundle)(&wkb)
	}
	return wkg
}

type writerMetadataFuture struct {
	writerMetadataCurrent
	// Override WriterMetadata.WKeys.
	WKeys tlfWriterKeyGenerationsFuture
	// Override WriterMetadata.Extra.
	Extra *writerMetadataExtraFuture `codec:"x,omitempty"`
}

func (wmf writerMetadataFuture) toCurrent() writerMetadataCurrent {
	wm := wmf.writerMetadataCurrent
	wm.WKeys = wmf.WKeys.toCurrent()
	// Maintain the invariant that Extra is either non-empty or
	// nil.
	//
	// TODO: Once we add fields to Extra, we'll have to change
	// this to:
	//
	// if wmf.Extra != nil {
	//   x := wmf.Extra.toCurrent()
	//   wm.Extra = &x
	// }
	wm.Extra = nil
	return wm
}

func (wmf writerMetadataFuture) toCurrentStruct() currentStruct {
	return wmf.toCurrent()
}

func makeFakeWriterMetadataFuture(t *testing.T) writerMetadataFuture {
	wmd := writerMetadataCurrent{
		[]byte{0xa, 0xb},
		"uid1",
		[]keybase1.UID{"uid1", "uid2"},
		nil,
		FakeTlfID(1, false),
		NullBranchID,
		0xa,
		100,
		99,
		101,
		nil,
	}
	wkb := makeFakeTLFWriterKeyBundleFuture(t)
	return writerMetadataFuture{
		wmd,
		tlfWriterKeyGenerationsFuture{&wkb},
		&writerMetadataExtraFuture{
			WriterMetadataExtra{
				UnknownFieldSet: codec.UnknownFieldSet{},
			},
			makeExtraOrBust("WriterMetadata", t),
		},
	}
}

func TestWriterMetadataUnknownFields(t *testing.T) {
	testStructUnknownFields(t, makeFakeWriterMetadataFuture(t))
}

type rootMetadataCurrent RootMetadata

func (rm rootMetadataCurrent) deepCopyStruct(f copyFields) currentStruct {
	return rootMetadataCurrent(RootMetadata(rm).deepCopyHelper(f))
}

type tlfReaderKeyGenerationsFuture []*tlfReaderKeyBundleFuture

func (rkgf tlfReaderKeyGenerationsFuture) toCurrent() TLFReaderKeyGenerations {
	rkg := make(TLFReaderKeyGenerations, len(rkgf))
	for i, rkbf := range rkgf {
		rkb := rkbf.toCurrent()
		rkg[i] = (*TLFReaderKeyBundle)(&rkb)
	}
	return rkg
}

// rootMetadataCurrentWrapper exists only to add extra depth to fields
// in rootMetadataCurrent, so that they may be overridden in
// rootMetadataFuture.
type rootMetadataCurrentWrapper struct {
	rootMetadataCurrent
}

type rootMetadataFuture struct {
	// Override RootMetadata.WriterMetadata. Put it first to work
	// around a bug in codec's field lookup code.
	//
	// TODO: Report and fix this bug upstream.
	writerMetadataFuture

	rootMetadataCurrentWrapper
	// Override RootMetadata.RKeys.
	RKeys tlfReaderKeyGenerationsFuture `codec:",omitempty"`
	extra
}

func (rmf rootMetadataFuture) toCurrent() rootMetadataCurrent {
	rm := rmf.rootMetadataCurrentWrapper.rootMetadataCurrent
	rm.WriterMetadata = WriterMetadata(rmf.writerMetadataFuture.toCurrent())
	rm.RKeys = rmf.RKeys.toCurrent()
	return rm
}

func (rmf rootMetadataFuture) toCurrentStruct() currentStruct {
	return rmf.toCurrent()
}

func makeFakeRootMetadataFuture(t *testing.T) rootMetadataFuture {
	wmf := makeFakeWriterMetadataFuture(t)
	rkb := makeFakeTLFReaderKeyBundleFuture(t)
	h, err := DefaultHash([]byte("fake buf"))
	require.Nil(t, err)
	rmf := rootMetadataFuture{
		wmf,
		rootMetadataCurrentWrapper{
			rootMetadataCurrent{
				WriterMetadata: WriterMetadata{},
				WriterMetadataSigInfo: SignatureInfo{
					100,
					[]byte{0xc},
					MakeFakeVerifyingKeyOrBust("fake kid"),
				},
				LastModifyingUser: "uid1",
				Flags:             0xb,
				Revision:          5,
				PrevRoot:          MdID{h},
				RKeys:             nil,
				UnresolvedReaders: nil,
				UnknownFieldSet:   codec.UnknownFieldSet{},
				data:              PrivateMetadata{},
				cachedTlfHandle:   nil,
				mdID:              MdID{},
				unverified:        false,
			},
		},
		[]*tlfReaderKeyBundleFuture{&rkb},
		makeExtraOrBust("RootMetadata", t),
	}
	_ = rmf.ID
	return rmf
}

func TestRootMetadataUnknownFields(t *testing.T) {
	testStructUnknownFields(t, makeFakeRootMetadataFuture(t))
}
<|endoftext|>