<commit_before>package lfs

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)

const Version = "0.5.1"

var (
	LargeSizeThreshold = 5 * 1024 * 1024
	TempDir            = filepath.Join(os.TempDir(), "git-lfs")
	UserAgent          string
	LocalWorkingDir    string
	LocalGitDir        string
	LocalMediaDir      string
	LocalLogDir        string
	checkedTempDir     string
)

func TempFile(prefix string) (*os.File, error) {
	if checkedTempDir != TempDir {
		if err := os.MkdirAll(TempDir, 0774); err != nil {
			return nil, err
		}
		checkedTempDir = TempDir
	}

	return ioutil.TempFile(TempDir, prefix)
}

func ResetTempDir() error {
	checkedTempDir = ""
	return os.RemoveAll(TempDir)
}

func LocalMediaPath(sha string) (string, error) {
	path := filepath.Join(LocalMediaDir, sha[0:2], sha[2:4])
	if err := os.MkdirAll(path, 0744); err != nil {
		return "", fmt.Errorf("Error trying to create local media directory in '%s': %s", path, err)
	}

	return filepath.Join(path, sha), nil
}

func Environ() []string {
	osEnviron := os.Environ()
	env := make([]string, 6, len(osEnviron)+6)
	env[0] = fmt.Sprintf("LocalWorkingDir=%s", LocalWorkingDir)
	env[1] = fmt.Sprintf("LocalGitDir=%s", LocalGitDir)
	env[2] = fmt.Sprintf("LocalMediaDir=%s", LocalMediaDir)
	env[3] = fmt.Sprintf("TempDir=%s", TempDir)
	env[4] = fmt.Sprintf("ConcurrentTransfers=%d", Config.ConcurrentTransfers())
	env[5] = fmt.Sprintf("BatchTransfer=%v", Config.BatchTransfer())

	for _, e := range osEnviron {
		if !strings.Contains(e, "GIT_") {
			continue
		}
		env = append(env, e)
	}

	return env
}

func InRepo() bool {
	return LocalWorkingDir != ""
}

func init() {
	var err error

	tracerx.DefaultKey = "GIT"
	tracerx.Prefix = "trace git-lfs: "

	LocalWorkingDir, LocalGitDir, err = resolveGitDir()
	if err == nil {
		LocalMediaDir = filepath.Join(LocalGitDir, "lfs", "objects")
		LocalLogDir = filepath.Join(LocalMediaDir, "logs")
		TempDir = filepath.Join(LocalGitDir, "lfs", "tmp")

		if err := os.MkdirAll(LocalMediaDir, 0744); err != nil {
			panic(fmt.Errorf("Error trying to create objects directory in '%s': %s", LocalMediaDir, err))
		}

		if err := os.MkdirAll(LocalLogDir, 0744); err != nil {
			panic(fmt.Errorf("Error trying to create log directory in '%s': %s", LocalLogDir, err))
		}

		if err := os.MkdirAll(TempDir, 0744); err != nil {
			panic(fmt.Errorf("Error trying to create temp directory in '%s': %s", TempDir, err))
		}

	}

	gitVersion, err := git.Config.Version()
	if err != nil {
		gitVersion = "unknown"
	}

	UserAgent = fmt.Sprintf("git-lfs/%s (GitHub; %s %s; git %s; go %s)", Version,
		runtime.GOOS,
		runtime.GOARCH,
		strings.Replace(gitVersion, "git version ", "", 1),
		strings.Replace(runtime.Version(), "go", "", 1))
}

func resolveGitDir() (string, string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", "", err
	}

	return recursiveResolveGitDir(wd)
}

func recursiveResolveGitDir(dir string) (string, string, error) {
	var cleanDir = filepath.Clean(dir)
	if cleanDir[len(cleanDir)-1] == os.PathSeparator {
		return "", "", fmt.Errorf("Git repository not found")
	}

	if filepath.Base(dir) == gitExt {
		// We're in the `.git` directory.  Make no assumptions about the working directory.
		return "", dir, nil
	}

	gitDir := filepath.Join(dir, gitExt)
	info, err := os.Stat(gitDir)
	if err != nil {
		// Found neither a directory nor a file named `.git`.
		// Move one directory up.
		return recursiveResolveGitDir(filepath.Dir(dir))
	}

	if !info.IsDir() {
		// Found a file named `.git` (we're in a submodule).
		return resolveDotGitFile(gitDir)
	}

	// Found the `.git` directory.
	return dir, gitDir, nil
}

func resolveDotGitFile(file string) (string, string, error) {
	// The local working directory is the directory the `.git` file is located in.
	wd := filepath.Dir(file)

	// The `.git` file tells us where the submodules `.git` directory is.
	gitDir, err := processDotGitFile(file)
	if err != nil {
		return "", "", err
	}

	return wd, gitDir, nil
}

func processDotGitFile(file string) (string, error) {
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	data := make([]byte, 512)
	n, err := f.Read(data)
	if err != nil {
		return "", err
	}

	contents := string(data[0:n])

	if !strings.HasPrefix(contents, gitPtrPrefix) {
		// The `.git` file has no entry telling us about gitdir.
		return "", nil
	}

	dir := strings.TrimSpace(strings.Split(contents, gitPtrPrefix)[1])

	if filepath.IsAbs(dir) {
		// The .git file contains an absolute path.
		return dir, nil
	}

	// The .git file contains a relative path.
	// Create an absolute path based on the directory the .git file is located in.
	absDir := filepath.Join(filepath.Dir(file), dir)

	return absDir, nil
}

const (
	gitExt       = ".git"
	gitPtrPrefix = "gitdir: "
)
<commit_msg>Used 0755 as file mode bits when creating directories<commit_after>package lfs

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)

const Version = "0.5.1"

var (
	LargeSizeThreshold = 5 * 1024 * 1024
	TempDir            = filepath.Join(os.TempDir(), "git-lfs")
	UserAgent          string
	LocalWorkingDir    string
	LocalGitDir        string
	LocalMediaDir      string
	LocalLogDir        string
	checkedTempDir     string
)

func TempFile(prefix string) (*os.File, error) {
	if checkedTempDir != TempDir {
		if err := os.MkdirAll(TempDir, 0755); err != nil {
			return nil, err
		}
		checkedTempDir = TempDir
	}

	return ioutil.TempFile(TempDir, prefix)
}

func ResetTempDir() error {
	checkedTempDir = ""
	return os.RemoveAll(TempDir)
}

func LocalMediaPath(sha string) (string, error) {
	path := filepath.Join(LocalMediaDir, sha[0:2], sha[2:4])
	if err := os.MkdirAll(path, 0755); err != nil {
		return "", fmt.Errorf("Error trying to create local media directory in '%s': %s", path, err)
	}

	return filepath.Join(path, sha), nil
}

func Environ() []string {
	osEnviron := os.Environ()
	env := make([]string, 6, len(osEnviron)+6)
	env[0] = fmt.Sprintf("LocalWorkingDir=%s", LocalWorkingDir)
	env[1] = fmt.Sprintf("LocalGitDir=%s", LocalGitDir)
	env[2] = fmt.Sprintf("LocalMediaDir=%s", LocalMediaDir)
	env[3] = fmt.Sprintf("TempDir=%s", TempDir)
	env[4] = fmt.Sprintf("ConcurrentTransfers=%d", Config.ConcurrentTransfers())
	env[5] = fmt.Sprintf("BatchTransfer=%v", Config.BatchTransfer())

	for _, e := range osEnviron {
		if !strings.Contains(e, "GIT_") {
			continue
		}
		env = append(env, e)
	}

	return env
}

func InRepo() bool {
	return LocalWorkingDir != ""
}

func init() {
	var err error

	tracerx.DefaultKey = "GIT"
	tracerx.Prefix = "trace git-lfs: "

	LocalWorkingDir, LocalGitDir, err = resolveGitDir()
	if err == nil {
		LocalMediaDir = filepath.Join(LocalGitDir, "lfs", "objects")
		LocalLogDir = filepath.Join(LocalMediaDir, "logs")
		TempDir = filepath.Join(LocalGitDir, "lfs", "tmp")

		if err := os.MkdirAll(LocalMediaDir, 0755); err != nil {
			panic(fmt.Errorf("Error trying to create objects directory in '%s': %s", LocalMediaDir, err))
		}

		if err := os.MkdirAll(LocalLogDir, 0755); err != nil {
			panic(fmt.Errorf("Error trying to create log directory in '%s': %s", LocalLogDir, err))
		}

		if err := os.MkdirAll(TempDir, 0755); err != nil {
			panic(fmt.Errorf("Error trying to create temp directory in '%s': %s", TempDir, err))
		}

	}

	gitVersion, err := git.Config.Version()
	if err != nil {
		gitVersion = "unknown"
	}

	UserAgent = fmt.Sprintf("git-lfs/%s (GitHub; %s %s; git %s; go %s)", Version,
		runtime.GOOS,
		runtime.GOARCH,
		strings.Replace(gitVersion, "git version ", "", 1),
		strings.Replace(runtime.Version(), "go", "", 1))
}

func resolveGitDir() (string, string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", "", err
	}

	return recursiveResolveGitDir(wd)
}

func recursiveResolveGitDir(dir string) (string, string, error) {
	var cleanDir = filepath.Clean(dir)
	if cleanDir[len(cleanDir)-1] == os.PathSeparator {
		return "", "", fmt.Errorf("Git repository not found")
	}

	if filepath.Base(dir) == gitExt {
		// We're in the `.git` directory.  Make no assumptions about the working directory.
		return "", dir, nil
	}

	gitDir := filepath.Join(dir, gitExt)
	info, err := os.Stat(gitDir)
	if err != nil {
		// Found neither a directory nor a file named `.git`.
		// Move one directory up.
		return recursiveResolveGitDir(filepath.Dir(dir))
	}

	if !info.IsDir() {
		// Found a file named `.git` (we're in a submodule).
		return resolveDotGitFile(gitDir)
	}

	// Found the `.git` directory.
	return dir, gitDir, nil
}

func resolveDotGitFile(file string) (string, string, error) {
	// The local working directory is the directory the `.git` file is located in.
	wd := filepath.Dir(file)

	// The `.git` file tells us where the submodules `.git` directory is.
	gitDir, err := processDotGitFile(file)
	if err != nil {
		return "", "", err
	}

	return wd, gitDir, nil
}

func processDotGitFile(file string) (string, error) {
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	data := make([]byte, 512)
	n, err := f.Read(data)
	if err != nil {
		return "", err
	}

	contents := string(data[0:n])

	if !strings.HasPrefix(contents, gitPtrPrefix) {
		// The `.git` file has no entry telling us about gitdir.
		return "", nil
	}

	dir := strings.TrimSpace(strings.Split(contents, gitPtrPrefix)[1])

	if filepath.IsAbs(dir) {
		// The .git file contains an absolute path.
		return dir, nil
	}

	// The .git file contains a relative path.
	// Create an absolute path based on the directory the .git file is located in.
	absDir := filepath.Join(filepath.Dir(file), dir)

	return absDir, nil
}

const (
	gitExt       = ".git"
	gitPtrPrefix = "gitdir: "
)
<|endoftext|>


<commit_before><commit_msg>mountinfo: docs nits<commit_after><|endoftext|>


<commit_before>package supervisor // import "github.com/docker/docker/libcontainerd/supervisor"

import (
	"context"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/services/server/config"
	"github.com/containerd/containerd/sys"
	"github.com/docker/docker/pkg/process"
	"github.com/docker/docker/pkg/system"
	"github.com/pelletier/go-toml"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const (
	maxConnectionRetryCount = 3
	healthCheckTimeout      = 3 * time.Second
	shutdownTimeout         = 15 * time.Second
	startupTimeout          = 15 * time.Second
	configFile              = "containerd.toml"
	binaryName              = "containerd"
	pidFile                 = "containerd.pid"
)

type remote struct {
	config.Config

	// configFile is the location where the generated containerd configuration
	// file is saved.
	configFile string

	daemonPid int
	pidFile   string
	logger    *logrus.Entry

	daemonWaitCh  chan struct{}
	daemonStartCh chan error
	daemonStopCh  chan struct{}

	stateDir string

	// oomScore adjusts the OOM score for the containerd process.
	oomScore int

	// logLevel overrides the containerd logging-level through the --log-level
	// command-line option.
	logLevel string
}

// Daemon represents a running containerd daemon
type Daemon interface {
	WaitTimeout(time.Duration) error
	Address() string
}

// DaemonOpt allows to configure parameters of container daemons
type DaemonOpt func(c *remote) error

// Start starts a containerd daemon and monitors it
func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Daemon, error) {
	r := &remote{
		stateDir: stateDir,
		Config: config.Config{
			Version: 2,
			Root:    filepath.Join(rootDir, "daemon"),
			State:   filepath.Join(stateDir, "daemon"),
		},
		configFile:    filepath.Join(stateDir, configFile),
		daemonPid:     -1,
		pidFile:       filepath.Join(stateDir, pidFile),
		logger:        logrus.WithField("module", "libcontainerd"),
		daemonStartCh: make(chan error, 1),
		daemonStopCh:  make(chan struct{}),
	}

	for _, opt := range opts {
		if err := opt(r); err != nil {
			return nil, err
		}
	}
	r.setDefaults()

	if err := system.MkdirAll(stateDir, 0700); err != nil {
		return nil, err
	}

	go r.monitorDaemon(ctx)

	timeout := time.NewTimer(startupTimeout)
	defer timeout.Stop()

	select {
	case <-timeout.C:
		return nil, errors.New("timeout waiting for containerd to start")
	case err := <-r.daemonStartCh:
		if err != nil {
			return nil, err
		}
	}

	return r, nil
}
func (r *remote) WaitTimeout(d time.Duration) error {
	timeout := time.NewTimer(d)
	defer timeout.Stop()

	select {
	case <-timeout.C:
		return errors.New("timeout waiting for containerd to stop")
	case <-r.daemonStopCh:
	}

	return nil
}

func (r *remote) Address() string {
	return r.GRPC.Address
}
func (r *remote) getContainerdPid() (int, error) {
	f, err := os.OpenFile(r.pidFile, os.O_RDWR, 0600)
	if err != nil {
		if os.IsNotExist(err) {
			return -1, nil
		}
		return -1, err
	}
	defer f.Close()

	b := make([]byte, 8)
	n, err := f.Read(b)
	if err != nil && err != io.EOF {
		return -1, err
	}

	if n > 0 {
		pid, err := strconv.ParseUint(string(b[:n]), 10, 64)
		if err != nil {
			return -1, err
		}
		if process.Alive(int(pid)) {
			return int(pid), nil
		}
	}

	return -1, nil
}

func (r *remote) getContainerdConfig() (string, error) {
	f, err := os.OpenFile(r.configFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
	if err != nil {
		return "", errors.Wrapf(err, "failed to open containerd config file (%s)", r.configFile)
	}
	defer f.Close()

	if err := toml.NewEncoder(f).Encode(r); err != nil {
		return "", errors.Wrapf(err, "failed to write containerd config file (%s)", r.configFile)
	}
	return r.configFile, nil
}

func (r *remote) startContainerd() error {
	pid, err := r.getContainerdPid()
	if err != nil {
		return err
	}

	if pid != -1 {
		r.daemonPid = pid
		r.logger.WithField("pid", pid).Infof("%s is still running", binaryName)
		return nil
	}

	configFile, err := r.getContainerdConfig()
	if err != nil {
		return err
	}

	args := []string{"--config", configFile}

	if r.logLevel != "" {
		args = append(args, "--log-level", r.logLevel)
	}

	cmd := exec.Command(binaryName, args...)
	// redirect containerd logs to docker logs
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = containerdSysProcAttr()
	// clear the NOTIFY_SOCKET from the env when starting containerd
	cmd.Env = nil
	for _, e := range os.Environ() {
		if !strings.HasPrefix(e, "NOTIFY_SOCKET") {
			cmd.Env = append(cmd.Env, e)
		}
	}

	startedCh := make(chan error)
	go func() {
		// On Linux, when cmd.SysProcAttr.Pdeathsig is set,
		// the signal is sent to the subprocess when the creating thread
		// terminates. The runtime terminates a thread if a goroutine
		// exits while locked to it. Prevent the containerd process
		// from getting killed prematurely by ensuring that the thread
		// used to start it remains alive until it or the daemon process
		// exits. See https://go.dev/issue/27505 for more details.
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()
		err := cmd.Start()
		startedCh <- err
		if err != nil {
			return
		}

		r.daemonWaitCh = make(chan struct{})
		// Reap our child when needed
		if err := cmd.Wait(); err != nil {
			r.logger.WithError(err).Errorf("containerd did not exit successfully")
		}
		close(r.daemonWaitCh)
	}()
	if err := <-startedCh; err != nil {
		return err
	}

	r.daemonPid = cmd.Process.Pid

	if err := r.adjustOOMScore(); err != nil {
		r.logger.WithError(err).Warn("failed to adjust OOM score")
	}

	err = os.WriteFile(r.pidFile, []byte(strconv.Itoa(r.daemonPid)), 0660)
	if err != nil {
		process.Kill(r.daemonPid)
		return errors.Wrap(err, "libcontainerd: failed to save daemon pid to disk")
	}

	r.logger.WithField("pid", r.daemonPid).WithField("address", r.Address()).Infof("started new %s process", binaryName)

	return nil
}

func (r *remote) adjustOOMScore() error {
	if r.oomScore == 0 || r.daemonPid <= 1 {
		// no score configured, or daemonPid contains an invalid PID (we don't
		// expect containerd to be running as PID 1 :)).
		return nil
	}
	if err := sys.SetOOMScore(r.daemonPid, r.oomScore); err != nil {
		return errors.Wrap(err, "failed to adjust OOM score for containerd process")
	}
	return nil
}

func (r *remote) monitorDaemon(ctx context.Context) {
	var (
		transientFailureCount = 0
		client                *containerd.Client
		err                   error
		delay                 time.Duration
		timer                 = time.NewTimer(0)
		started               bool
	)

	defer func() {
		if r.daemonPid != -1 {
			r.stopDaemon()
		}

		// cleanup some files
		_ = os.Remove(r.pidFile)

		r.platformCleanup()

		close(r.daemonStopCh)
		timer.Stop()
	}()

	// ensure no races on sending to timer.C even though there is a 0 duration.
	if !timer.Stop() {
		<-timer.C
	}

	for {
		timer.Reset(delay)

		select {
		case <-ctx.Done():
			r.logger.Info("stopping healthcheck following graceful shutdown")
			if client != nil {
				client.Close()
			}
			return
		case <-timer.C:
		}

		if r.daemonPid == -1 {
			if r.daemonWaitCh != nil {
				select {
				case <-ctx.Done():
					r.logger.Info("stopping containerd startup following graceful shutdown")
					return
				case <-r.daemonWaitCh:
				}
			}

			os.RemoveAll(r.GRPC.Address)
			if err := r.startContainerd(); err != nil {
				if !started {
					r.daemonStartCh <- err
					return
				}
				r.logger.WithError(err).Error("failed restarting containerd")
				delay = 50 * time.Millisecond
				continue
			}

			client, err = containerd.New(r.GRPC.Address, containerd.WithTimeout(60*time.Second))
			if err != nil {
				r.logger.WithError(err).Error("failed connecting to containerd")
				delay = 100 * time.Millisecond
				continue
			}
			r.logger.WithField("address", r.GRPC.Address).Debug("created containerd monitoring client")
		}

		if client != nil {
			tctx, cancel := context.WithTimeout(ctx, healthCheckTimeout)
			_, err := client.IsServing(tctx)
			cancel()
			if err == nil {
				if !started {
					close(r.daemonStartCh)
					started = true
				}

				transientFailureCount = 0

				select {
				case <-r.daemonWaitCh:
				case <-ctx.Done():
				}

				// Set a small delay in case there is a recurring failure (or bug in this code)
				// to ensure we don't end up in a super tight loop.
				delay = 500 * time.Millisecond
				continue
			}

			r.logger.WithError(err).WithField("binary", binaryName).Debug("daemon is not responding")

			transientFailureCount++
			if transientFailureCount < maxConnectionRetryCount || process.Alive(r.daemonPid) {
				delay = time.Duration(transientFailureCount) * 200 * time.Millisecond
				continue
			}
			client.Close()
			client = nil
		}

		if process.Alive(r.daemonPid) {
			r.logger.WithField("pid", r.daemonPid).Info("killing and restarting containerd")
			r.killDaemon()
		}

		r.daemonPid = -1
		delay = 0
		transientFailureCount = 0
	}
}
<commit_msg>libcontainerd/supervisor: use pkg/pidfile for reading and writing pidfile<commit_after>package supervisor // import "github.com/docker/docker/libcontainerd/supervisor"

import (
	"context"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/services/server/config"
	"github.com/containerd/containerd/sys"
	"github.com/docker/docker/pkg/pidfile"
	"github.com/docker/docker/pkg/process"
	"github.com/docker/docker/pkg/system"
	"github.com/pelletier/go-toml"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const (
	maxConnectionRetryCount = 3
	healthCheckTimeout      = 3 * time.Second
	shutdownTimeout         = 15 * time.Second
	startupTimeout          = 15 * time.Second
	configFile              = "containerd.toml"
	binaryName              = "containerd"
	pidFile                 = "containerd.pid"
)

type remote struct {
	config.Config

	// configFile is the location where the generated containerd configuration
	// file is saved.
	configFile string

	daemonPid int
	pidFile   string
	logger    *logrus.Entry

	daemonWaitCh  chan struct{}
	daemonStartCh chan error
	daemonStopCh  chan struct{}

	stateDir string

	// oomScore adjusts the OOM score for the containerd process.
	oomScore int

	// logLevel overrides the containerd logging-level through the --log-level
	// command-line option.
	logLevel string
}

// Daemon represents a running containerd daemon
type Daemon interface {
	WaitTimeout(time.Duration) error
	Address() string
}

// DaemonOpt allows to configure parameters of container daemons
type DaemonOpt func(c *remote) error

// Start starts a containerd daemon and monitors it
func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Daemon, error) {
	r := &remote{
		stateDir: stateDir,
		Config: config.Config{
			Version: 2,
			Root:    filepath.Join(rootDir, "daemon"),
			State:   filepath.Join(stateDir, "daemon"),
		},
		configFile:    filepath.Join(stateDir, configFile),
		daemonPid:     -1,
		pidFile:       filepath.Join(stateDir, pidFile),
		logger:        logrus.WithField("module", "libcontainerd"),
		daemonStartCh: make(chan error, 1),
		daemonStopCh:  make(chan struct{}),
	}

	for _, opt := range opts {
		if err := opt(r); err != nil {
			return nil, err
		}
	}
	r.setDefaults()

	if err := system.MkdirAll(stateDir, 0700); err != nil {
		return nil, err
	}

	go r.monitorDaemon(ctx)

	timeout := time.NewTimer(startupTimeout)
	defer timeout.Stop()

	select {
	case <-timeout.C:
		return nil, errors.New("timeout waiting for containerd to start")
	case err := <-r.daemonStartCh:
		if err != nil {
			return nil, err
		}
	}

	return r, nil
}
func (r *remote) WaitTimeout(d time.Duration) error {
	timeout := time.NewTimer(d)
	defer timeout.Stop()

	select {
	case <-timeout.C:
		return errors.New("timeout waiting for containerd to stop")
	case <-r.daemonStopCh:
	}

	return nil
}

func (r *remote) Address() string {
	return r.GRPC.Address
}

func (r *remote) getContainerdConfig() (string, error) {
	f, err := os.OpenFile(r.configFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
	if err != nil {
		return "", errors.Wrapf(err, "failed to open containerd config file (%s)", r.configFile)
	}
	defer f.Close()

	if err := toml.NewEncoder(f).Encode(r); err != nil {
		return "", errors.Wrapf(err, "failed to write containerd config file (%s)", r.configFile)
	}
	return r.configFile, nil
}

func (r *remote) startContainerd() error {
	pid, err := pidfile.Read(r.pidFile)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return err
	}

	if pid > 0 {
		r.daemonPid = pid
		r.logger.WithField("pid", pid).Infof("%s is still running", binaryName)
		return nil
	}

	cfgFile, err := r.getContainerdConfig()
	if err != nil {
		return err
	}
	args := []string{"--config", cfgFile}

	if r.logLevel != "" {
		args = append(args, "--log-level", r.logLevel)
	}

	cmd := exec.Command(binaryName, args...)
	// redirect containerd logs to docker logs
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = containerdSysProcAttr()
	// clear the NOTIFY_SOCKET from the env when starting containerd
	cmd.Env = nil
	for _, e := range os.Environ() {
		if !strings.HasPrefix(e, "NOTIFY_SOCKET") {
			cmd.Env = append(cmd.Env, e)
		}
	}

	startedCh := make(chan error)
	go func() {
		// On Linux, when cmd.SysProcAttr.Pdeathsig is set,
		// the signal is sent to the subprocess when the creating thread
		// terminates. The runtime terminates a thread if a goroutine
		// exits while locked to it. Prevent the containerd process
		// from getting killed prematurely by ensuring that the thread
		// used to start it remains alive until it or the daemon process
		// exits. See https://go.dev/issue/27505 for more details.
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()
		err := cmd.Start()
		startedCh <- err
		if err != nil {
			return
		}

		r.daemonWaitCh = make(chan struct{})
		// Reap our child when needed
		if err := cmd.Wait(); err != nil {
			r.logger.WithError(err).Errorf("containerd did not exit successfully")
		}
		close(r.daemonWaitCh)
	}()
	if err := <-startedCh; err != nil {
		return err
	}

	r.daemonPid = cmd.Process.Pid

	if err := r.adjustOOMScore(); err != nil {
		r.logger.WithError(err).Warn("failed to adjust OOM score")
	}

	err = pidfile.Write(r.pidFile, r.daemonPid)
	if err != nil {
		process.Kill(r.daemonPid)
		return errors.Wrap(err, "libcontainerd: failed to save daemon pid to disk")
	}

	r.logger.WithField("pid", r.daemonPid).WithField("address", r.Address()).Infof("started new %s process", binaryName)

	return nil
}

func (r *remote) adjustOOMScore() error {
	if r.oomScore == 0 || r.daemonPid <= 1 {
		// no score configured, or daemonPid contains an invalid PID (we don't
		// expect containerd to be running as PID 1 :)).
		return nil
	}
	if err := sys.SetOOMScore(r.daemonPid, r.oomScore); err != nil {
		return errors.Wrap(err, "failed to adjust OOM score for containerd process")
	}
	return nil
}

func (r *remote) monitorDaemon(ctx context.Context) {
	var (
		transientFailureCount = 0
		client                *containerd.Client
		err                   error
		delay                 time.Duration
		timer                 = time.NewTimer(0)
		started               bool
	)

	defer func() {
		if r.daemonPid != -1 {
			r.stopDaemon()
		}

		// cleanup some files
		_ = os.Remove(r.pidFile)

		r.platformCleanup()

		close(r.daemonStopCh)
		timer.Stop()
	}()

	// ensure no races on sending to timer.C even though there is a 0 duration.
	if !timer.Stop() {
		<-timer.C
	}

	for {
		timer.Reset(delay)

		select {
		case <-ctx.Done():
			r.logger.Info("stopping healthcheck following graceful shutdown")
			if client != nil {
				client.Close()
			}
			return
		case <-timer.C:
		}

		if r.daemonPid == -1 {
			if r.daemonWaitCh != nil {
				select {
				case <-ctx.Done():
					r.logger.Info("stopping containerd startup following graceful shutdown")
					return
				case <-r.daemonWaitCh:
				}
			}

			os.RemoveAll(r.GRPC.Address)
			if err := r.startContainerd(); err != nil {
				if !started {
					r.daemonStartCh <- err
					return
				}
				r.logger.WithError(err).Error("failed restarting containerd")
				delay = 50 * time.Millisecond
				continue
			}

			client, err = containerd.New(r.GRPC.Address, containerd.WithTimeout(60*time.Second))
			if err != nil {
				r.logger.WithError(err).Error("failed connecting to containerd")
				delay = 100 * time.Millisecond
				continue
			}
			r.logger.WithField("address", r.GRPC.Address).Debug("created containerd monitoring client")
		}

		if client != nil {
			tctx, cancel := context.WithTimeout(ctx, healthCheckTimeout)
			_, err := client.IsServing(tctx)
			cancel()
			if err == nil {
				if !started {
					close(r.daemonStartCh)
					started = true
				}

				transientFailureCount = 0

				select {
				case <-r.daemonWaitCh:
				case <-ctx.Done():
				}

				// Set a small delay in case there is a recurring failure (or bug in this code)
				// to ensure we don't end up in a super tight loop.
				delay = 500 * time.Millisecond
				continue
			}

			r.logger.WithError(err).WithField("binary", binaryName).Debug("daemon is not responding")

			transientFailureCount++
			if transientFailureCount < maxConnectionRetryCount || process.Alive(r.daemonPid) {
				delay = time.Duration(transientFailureCount) * 200 * time.Millisecond
				continue
			}
			client.Close()
			client = nil
		}

		if process.Alive(r.daemonPid) {
			r.logger.WithField("pid", r.daemonPid).Info("killing and restarting containerd")
			r.killDaemon()
		}

		r.daemonPid = -1
		delay = 0
		transientFailureCount = 0
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package apiclient

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	clientset "k8s.io/client-go/kubernetes"
	fakeclientset "k8s.io/client-go/kubernetes/fake"
	core "k8s.io/client-go/testing"
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)

// DryRunGetter is an interface that must be supplied to the NewDryRunClient function in order to contstruct a fully functional fake dryrun clientset
type DryRunGetter interface {
	HandleGetAction(core.GetAction) (bool, runtime.Object, error)
	HandleListAction(core.ListAction) (bool, runtime.Object, error)
}

// MarshalFunc takes care of converting any object to a byte array for displaying the object to the user
type MarshalFunc func(runtime.Object, schema.GroupVersion) ([]byte, error)

// DefaultMarshalFunc is the default MarshalFunc used; uses YAML to print objects to the user
func DefaultMarshalFunc(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) {
	return kubeadmutil.MarshalToYaml(obj, gv)
}

// DryRunClientOptions specifies options to pass to NewDryRunClientWithOpts in order to get a dryrun clientset
type DryRunClientOptions struct {
	Writer          io.Writer
	Getter          DryRunGetter
	PrependReactors []core.Reactor
	AppendReactors  []core.Reactor
	MarshalFunc     MarshalFunc
	PrintGETAndLIST bool
}

// GetDefaultDryRunClientOptions returns the default DryRunClientOptions values
func GetDefaultDryRunClientOptions(drg DryRunGetter, w io.Writer) DryRunClientOptions {
	return DryRunClientOptions{
		Writer:          w,
		Getter:          drg,
		PrependReactors: []core.Reactor{},
		AppendReactors:  []core.Reactor{},
		MarshalFunc:     DefaultMarshalFunc,
		PrintGETAndLIST: false,
	}
}

// actionWithName is the generic interface for an action that has a name associated with it
// This just makes it easier to catch all actions that has a name; instead of hard-coding all request that has it associated
type actionWithName interface {
	core.Action
	GetName() string
}

// actionWithObject is the generic interface for an action that has an object associated with it
// This just makes it easier to catch all actions that has an object; instead of hard-coding all request that has it associated
type actionWithObject interface {
	core.Action
	GetObject() runtime.Object
}

// NewDryRunClient is a wrapper for NewDryRunClientWithOpts using some default values
func NewDryRunClient(drg DryRunGetter, w io.Writer) clientset.Interface {
	return NewDryRunClientWithOpts(GetDefaultDryRunClientOptions(drg, w))
}

// NewDryRunClientWithOpts returns a clientset.Interface that can be used normally for talking to the Kubernetes API.
// This client doesn't apply changes to the backend. The client gets GET/LIST values from the DryRunGetter implementation.
// This client logs all I/O to the writer w in YAML format
func NewDryRunClientWithOpts(opts DryRunClientOptions) clientset.Interface {
	// Build a chain of reactors to act like a normal clientset; but log everything that is happening and don't change any state
	client := fakeclientset.NewSimpleClientset()

	// Build the chain of reactors. Order matters; first item here will be invoked first on match, then the second one will be evaluated, etc.
	defaultReactorChain := []core.Reactor{
		// Log everything that happens. Default the object if it's about to be created/updated so that the logged object is representative.
		&core.SimpleReactor{
			Verb:     "*",
			Resource: "*",
			Reaction: func(action core.Action) (bool, runtime.Object, error) {
				logDryRunAction(action, opts.Writer, opts.MarshalFunc)

				return false, nil, nil
			},
		},
		// Let the DryRunGetter implementation take care of all GET requests.
		// The DryRunGetter implementation may call a real API Server behind the scenes or just fake everything
		&core.SimpleReactor{
			Verb:     "get",
			Resource: "*",
			Reaction: func(action core.Action) (bool, runtime.Object, error) {
				getAction, ok := action.(core.GetAction)
				if !ok {
					// something's wrong, we can't handle this event
					return true, nil, errors.New("can't cast get reactor event action object to GetAction interface")
				}
				handled, obj, err := opts.Getter.HandleGetAction(getAction)

				if opts.PrintGETAndLIST {
					// Print the marshalled object format with one tab indentation
					objBytes, err := opts.MarshalFunc(obj, action.GetResource().GroupVersion())
					if err == nil {
						fmt.Println("[dryrun] Returning faked GET response:")
						PrintBytesWithLinePrefix(opts.Writer, objBytes, "\t")
					}
				}

				return handled, obj, err
			},
		},
		// Let the DryRunGetter implementation take care of all GET requests.
		// The DryRunGetter implementation may call a real API Server behind the scenes or just fake everything
		&core.SimpleReactor{
			Verb:     "list",
			Resource: "*",
			Reaction: func(action core.Action) (bool, runtime.Object, error) {
				listAction, ok := action.(core.ListAction)
				if !ok {
					// something's wrong, we can't handle this event
					return true, nil, errors.New("can't cast list reactor event action object to ListAction interface")
				}
				handled, objs, err := opts.Getter.HandleListAction(listAction)

				if opts.PrintGETAndLIST {
					// Print the marshalled object format with one tab indentation
					objBytes, err := opts.MarshalFunc(objs, action.GetResource().GroupVersion())
					if err == nil {
						fmt.Println("[dryrun] Returning faked LIST response:")
						PrintBytesWithLinePrefix(opts.Writer, objBytes, "\t")
					}
				}

				return handled, objs, err
			},
		},
		// For the verbs that modify anything on the server; just return the object if present and exit successfully
		&core.SimpleReactor{
			Verb:     "create",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "update",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "delete",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "delete-collection",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "patch",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
	}

	// The chain of reactors will look like this:
	// opts.PrependReactors | defaultReactorChain | opts.AppendReactors | client.Fake.ReactionChain (default reactors for the fake clientset)
	fullReactorChain := append(opts.PrependReactors, defaultReactorChain...)
	fullReactorChain = append(fullReactorChain, opts.AppendReactors...)

	// Prepend the reaction chain with our reactors. Important, these MUST be prepended; not appended due to how the fake clientset works by default
	client.Fake.ReactionChain = append(fullReactorChain, client.Fake.ReactionChain...)
	return client
}

// successfulModificationReactorFunc is a no-op that just returns the POSTed/PUTed value if present; but does nothing to edit any backing data store.
func successfulModificationReactorFunc(action core.Action) (bool, runtime.Object, error) {
	objAction, ok := action.(actionWithObject)
	if ok {
		return true, objAction.GetObject(), nil
	}
	return true, nil, nil
}

// logDryRunAction logs the action that was recorded by the "catch-all" (*,*) reactor and tells the user what would have happened in an user-friendly way
func logDryRunAction(action core.Action, w io.Writer, marshalFunc MarshalFunc) {

	group := action.GetResource().Group
	if len(group) == 0 {
		group = "core"
	}
	fmt.Fprintf(w, "[dryrun] Would perform action %s on resource %q in API group \"%s/%s\"\n", strings.ToUpper(action.GetVerb()), action.GetResource().Resource, group, action.GetResource().Version)

	namedAction, ok := action.(actionWithName)
	if ok {
		fmt.Fprintf(w, "[dryrun] Resource name: %q\n", namedAction.GetName())
	}

	objAction, ok := action.(actionWithObject)
	if ok && objAction.GetObject() != nil {
		// Print the marshalled object with a tab indentation
		objBytes, err := marshalFunc(objAction.GetObject(), action.GetResource().GroupVersion())
		if err == nil {
			fmt.Println("[dryrun] Attached object:")
			PrintBytesWithLinePrefix(w, objBytes, "\t")
		}
	}

	patchAction, ok := action.(core.PatchAction)
	if ok {
		// Replace all occurrences of \" with a simple " when printing
		fmt.Fprintf(w, "[dryrun] Attached patch:\n\t%s\n", strings.Replace(string(patchAction.GetPatch()), `\"`, `"`, -1))
	}
}

// PrintBytesWithLinePrefix prints objBytes to writer w with linePrefix in the beginning of every line
func PrintBytesWithLinePrefix(w io.Writer, objBytes []byte, linePrefix string) {
	scanner := bufio.NewScanner(bytes.NewReader(objBytes))
	for scanner.Scan() {
		fmt.Fprintf(w, "%s%s\n", linePrefix, scanner.Text())
	}
}
<commit_msg>cleanup: fix typo "contstruct" -> "construct"<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package apiclient

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	clientset "k8s.io/client-go/kubernetes"
	fakeclientset "k8s.io/client-go/kubernetes/fake"
	core "k8s.io/client-go/testing"
	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)

// DryRunGetter is an interface that must be supplied to the NewDryRunClient function in order to construct a fully functional fake dryrun clientset
type DryRunGetter interface {
	HandleGetAction(core.GetAction) (bool, runtime.Object, error)
	HandleListAction(core.ListAction) (bool, runtime.Object, error)
}

// MarshalFunc takes care of converting any object to a byte array for displaying the object to the user
type MarshalFunc func(runtime.Object, schema.GroupVersion) ([]byte, error)

// DefaultMarshalFunc is the default MarshalFunc used; uses YAML to print objects to the user
func DefaultMarshalFunc(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) {
	return kubeadmutil.MarshalToYaml(obj, gv)
}

// DryRunClientOptions specifies options to pass to NewDryRunClientWithOpts in order to get a dryrun clientset
type DryRunClientOptions struct {
	Writer          io.Writer
	Getter          DryRunGetter
	PrependReactors []core.Reactor
	AppendReactors  []core.Reactor
	MarshalFunc     MarshalFunc
	PrintGETAndLIST bool
}

// GetDefaultDryRunClientOptions returns the default DryRunClientOptions values
func GetDefaultDryRunClientOptions(drg DryRunGetter, w io.Writer) DryRunClientOptions {
	return DryRunClientOptions{
		Writer:          w,
		Getter:          drg,
		PrependReactors: []core.Reactor{},
		AppendReactors:  []core.Reactor{},
		MarshalFunc:     DefaultMarshalFunc,
		PrintGETAndLIST: false,
	}
}

// actionWithName is the generic interface for an action that has a name associated with it
// This just makes it easier to catch all actions that has a name; instead of hard-coding all request that has it associated
type actionWithName interface {
	core.Action
	GetName() string
}

// actionWithObject is the generic interface for an action that has an object associated with it
// This just makes it easier to catch all actions that has an object; instead of hard-coding all request that has it associated
type actionWithObject interface {
	core.Action
	GetObject() runtime.Object
}

// NewDryRunClient is a wrapper for NewDryRunClientWithOpts using some default values
func NewDryRunClient(drg DryRunGetter, w io.Writer) clientset.Interface {
	return NewDryRunClientWithOpts(GetDefaultDryRunClientOptions(drg, w))
}

// NewDryRunClientWithOpts returns a clientset.Interface that can be used normally for talking to the Kubernetes API.
// This client doesn't apply changes to the backend. The client gets GET/LIST values from the DryRunGetter implementation.
// This client logs all I/O to the writer w in YAML format
func NewDryRunClientWithOpts(opts DryRunClientOptions) clientset.Interface {
	// Build a chain of reactors to act like a normal clientset; but log everything that is happening and don't change any state
	client := fakeclientset.NewSimpleClientset()

	// Build the chain of reactors. Order matters; first item here will be invoked first on match, then the second one will be evaluated, etc.
	defaultReactorChain := []core.Reactor{
		// Log everything that happens. Default the object if it's about to be created/updated so that the logged object is representative.
		&core.SimpleReactor{
			Verb:     "*",
			Resource: "*",
			Reaction: func(action core.Action) (bool, runtime.Object, error) {
				logDryRunAction(action, opts.Writer, opts.MarshalFunc)

				return false, nil, nil
			},
		},
		// Let the DryRunGetter implementation take care of all GET requests.
		// The DryRunGetter implementation may call a real API Server behind the scenes or just fake everything
		&core.SimpleReactor{
			Verb:     "get",
			Resource: "*",
			Reaction: func(action core.Action) (bool, runtime.Object, error) {
				getAction, ok := action.(core.GetAction)
				if !ok {
					// something's wrong, we can't handle this event
					return true, nil, errors.New("can't cast get reactor event action object to GetAction interface")
				}
				handled, obj, err := opts.Getter.HandleGetAction(getAction)

				if opts.PrintGETAndLIST {
					// Print the marshalled object format with one tab indentation
					objBytes, err := opts.MarshalFunc(obj, action.GetResource().GroupVersion())
					if err == nil {
						fmt.Println("[dryrun] Returning faked GET response:")
						PrintBytesWithLinePrefix(opts.Writer, objBytes, "\t")
					}
				}

				return handled, obj, err
			},
		},
		// Let the DryRunGetter implementation take care of all GET requests.
		// The DryRunGetter implementation may call a real API Server behind the scenes or just fake everything
		&core.SimpleReactor{
			Verb:     "list",
			Resource: "*",
			Reaction: func(action core.Action) (bool, runtime.Object, error) {
				listAction, ok := action.(core.ListAction)
				if !ok {
					// something's wrong, we can't handle this event
					return true, nil, errors.New("can't cast list reactor event action object to ListAction interface")
				}
				handled, objs, err := opts.Getter.HandleListAction(listAction)

				if opts.PrintGETAndLIST {
					// Print the marshalled object format with one tab indentation
					objBytes, err := opts.MarshalFunc(objs, action.GetResource().GroupVersion())
					if err == nil {
						fmt.Println("[dryrun] Returning faked LIST response:")
						PrintBytesWithLinePrefix(opts.Writer, objBytes, "\t")
					}
				}

				return handled, objs, err
			},
		},
		// For the verbs that modify anything on the server; just return the object if present and exit successfully
		&core.SimpleReactor{
			Verb:     "create",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "update",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "delete",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "delete-collection",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
		&core.SimpleReactor{
			Verb:     "patch",
			Resource: "*",
			Reaction: successfulModificationReactorFunc,
		},
	}

	// The chain of reactors will look like this:
	// opts.PrependReactors | defaultReactorChain | opts.AppendReactors | client.Fake.ReactionChain (default reactors for the fake clientset)
	fullReactorChain := append(opts.PrependReactors, defaultReactorChain...)
	fullReactorChain = append(fullReactorChain, opts.AppendReactors...)

	// Prepend the reaction chain with our reactors. Important, these MUST be prepended; not appended due to how the fake clientset works by default
	client.Fake.ReactionChain = append(fullReactorChain, client.Fake.ReactionChain...)
	return client
}

// successfulModificationReactorFunc is a no-op that just returns the POSTed/PUTed value if present; but does nothing to edit any backing data store.
func successfulModificationReactorFunc(action core.Action) (bool, runtime.Object, error) {
	objAction, ok := action.(actionWithObject)
	if ok {
		return true, objAction.GetObject(), nil
	}
	return true, nil, nil
}

// logDryRunAction logs the action that was recorded by the "catch-all" (*,*) reactor and tells the user what would have happened in an user-friendly way
func logDryRunAction(action core.Action, w io.Writer, marshalFunc MarshalFunc) {

	group := action.GetResource().Group
	if len(group) == 0 {
		group = "core"
	}
	fmt.Fprintf(w, "[dryrun] Would perform action %s on resource %q in API group \"%s/%s\"\n", strings.ToUpper(action.GetVerb()), action.GetResource().Resource, group, action.GetResource().Version)

	namedAction, ok := action.(actionWithName)
	if ok {
		fmt.Fprintf(w, "[dryrun] Resource name: %q\n", namedAction.GetName())
	}

	objAction, ok := action.(actionWithObject)
	if ok && objAction.GetObject() != nil {
		// Print the marshalled object with a tab indentation
		objBytes, err := marshalFunc(objAction.GetObject(), action.GetResource().GroupVersion())
		if err == nil {
			fmt.Println("[dryrun] Attached object:")
			PrintBytesWithLinePrefix(w, objBytes, "\t")
		}
	}

	patchAction, ok := action.(core.PatchAction)
	if ok {
		// Replace all occurrences of \" with a simple " when printing
		fmt.Fprintf(w, "[dryrun] Attached patch:\n\t%s\n", strings.Replace(string(patchAction.GetPatch()), `\"`, `"`, -1))
	}
}

// PrintBytesWithLinePrefix prints objBytes to writer w with linePrefix in the beginning of every line
func PrintBytesWithLinePrefix(w io.Writer, objBytes []byte, linePrefix string) {
	scanner := bufio.NewScanner(bytes.NewReader(objBytes))
	for scanner.Scan() {
		fmt.Fprintf(w, "%s%s\n", linePrefix, scanner.Text())
	}
}
<|endoftext|>


<commit_before>package blx

import (
	"testing"

	"github.com/davecgh/go-spew/spew"

	"github.com/nimona/go-nimona/mesh"
	"github.com/stretchr/testify/mock"

	suite "github.com/stretchr/testify/suite"
)

type blxTestSuite struct {
	suite.Suite
	mockPubSub *mesh.MockPubSub
	messages   chan interface{}
	blx        *blockExchange
}

func (suite *blxTestSuite) SetupTest() {
	suite.mockPubSub = &mesh.MockPubSub{}
	suite.messages = make(chan interface{}, 10)
	suite.mockPubSub.On("Subscribe", "blx:.*").Return(suite.messages, nil)
	bootstrapMutation := mutation.PeerProtocolDiscovered{
		PeerID:          "bootstrap",
		ProtocolName:    "messaging",
		ProtocolAddress: "bootstrap-address",
		Pinned:          true,
	}
	suite.mockPubSub.On("Publish", bootstrapMutation,
		mutation.PeerProtocolDiscoveredTopic).Return(nil)

	suite.blx, _ = NewBlockExchange(suite.mockPubSub)
}

func (suite *blxTestSuite) TestReceiveMessage() {
	suite.mockPubSub.
		On("Publish", mock.AnythingOfType("mesh.Message"), "message:send").
		Return(nil).
		Run(func(args mock.Arguments) {
			reqPublishedMessage := args.Get(0).(mesh.Message)
			spew.Println(reqPublishedMessage)
		})

	err := suite.blx.Send("test02", "test01",
		[]byte("test"), map[string][]byte{})
	suite.NoError(err)
}

func TestRunBlxTestSuite(t *testing.T) {
	suite.Run(t, new(blxTestSuite))
}
<commit_msg>Remove test<commit_after><|endoftext|>


<commit_before>package lib

import (
	"errors"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
)

type Service struct {
	Name *string
}

type ECS struct {
	svc ecsiface.ECSAPI
}

func NewECS(awsConfig *aws.Config) *ECS {
	return &ECS{
		svc: ecs.New(session.New(), awsConfig),
	}
}

func (e *ECS) RegisterTaskDefinition(registerTaskDefinitionInput *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) {
	return e.svc.RegisterTaskDefinition(registerTaskDefinitionInput)
}

func (e *ECS) UpsertService(createServiceInput *ecs.CreateServiceInput) (interface{}, error) {
	if createServiceInput.Cluster == nil {
		createServiceInput.Cluster = aws.String("default")
	}
	ok, err := e.isExistService(*createServiceInput.Cluster, *createServiceInput.ServiceName)
	if err != nil {
		return nil, err
	}
	if !ok {
		return e.svc.CreateService(createServiceInput)
	}

	updateServiceInput := &ecs.UpdateServiceInput{}
	updateServiceInput.Cluster = createServiceInput.Cluster
	updateServiceInput.DeploymentConfiguration = createServiceInput.DeploymentConfiguration
	updateServiceInput.DesiredCount = createServiceInput.DesiredCount
	updateServiceInput.Service = createServiceInput.ServiceName
	updateServiceInput.TaskDefinition = createServiceInput.TaskDefinition
	return e.svc.UpdateService(updateServiceInput)
}

func (e *ECS) ListServices(params *ecs.ListServicesInput) (*ecs.ListServicesOutput, error) {
	var (
		ret     = &ecs.ListServicesOutput{}
		pageNum int
	)

	err := e.svc.ListServicesPages(params, func(page *ecs.ListServicesOutput, lastPage bool) bool {
		pageNum++
		ret.ServiceArns = append(ret.ServiceArns, page.ServiceArns...)
		return pageNum <= 1000
	})
	if err != nil {
		return nil, err
	}
	return ret, nil
}

func (e *ECS) isExistService(clusetrName string, serviceName string) (bool, error) {
	listInput := &ecs.ListServicesInput{
		Cluster: aws.String(clusetrName),
	}
	result, err := e.ListServices(listInput)
	if err != nil {
		return false, err
	}
	for _, v := range result.ServiceArns {
		s, err := parseArn(v)
		if err != nil {
			return false, err
		}
		if *s.Name == serviceName {
			return true, nil
		}
	}
	return false, nil
}

func parseArn(arn *string) (*Service, error) {
	splitStr := strings.Split(*arn, "service/")
	if len(splitStr) != 2 {
		return nil, errors.New("illegal arn string")
	}
	return &Service{&splitStr[1]}, nil
}
<commit_msg>関数名変更<commit_after>package lib

import (
	"errors"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
)

type Service struct {
	Name *string
}

type ECS struct {
	svc ecsiface.ECSAPI
}

func NewECS(awsConfig *aws.Config) *ECS {
	return &ECS{
		svc: ecs.New(session.New(), awsConfig),
	}
}

func (e *ECS) RegisterTaskDefinition(registerTaskDefinitionInput *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) {
	return e.svc.RegisterTaskDefinition(registerTaskDefinitionInput)
}

func (e *ECS) UpsertService(createServiceInput *ecs.CreateServiceInput) (interface{}, error) {
	if createServiceInput.Cluster == nil {
		createServiceInput.Cluster = aws.String("default")
	}
	ok, err := e.serviceExists(*createServiceInput.Cluster, *createServiceInput.ServiceName)
	if err != nil {
		return nil, err
	}
	if !ok {
		return e.svc.CreateService(createServiceInput)
	}

	updateServiceInput := &ecs.UpdateServiceInput{}
	updateServiceInput.Cluster = createServiceInput.Cluster
	updateServiceInput.DeploymentConfiguration = createServiceInput.DeploymentConfiguration
	updateServiceInput.DesiredCount = createServiceInput.DesiredCount
	updateServiceInput.Service = createServiceInput.ServiceName
	updateServiceInput.TaskDefinition = createServiceInput.TaskDefinition
	return e.svc.UpdateService(updateServiceInput)
}

func (e *ECS) ListServices(params *ecs.ListServicesInput) (*ecs.ListServicesOutput, error) {
	var (
		ret     = &ecs.ListServicesOutput{}
		pageNum int
	)

	err := e.svc.ListServicesPages(params, func(page *ecs.ListServicesOutput, lastPage bool) bool {
		pageNum++
		ret.ServiceArns = append(ret.ServiceArns, page.ServiceArns...)
		return pageNum <= 1000
	})
	if err != nil {
		return nil, err
	}
	return ret, nil
}

func (e *ECS) serviceExists(clusetrName string, serviceName string) (bool, error) {
	listInput := &ecs.ListServicesInput{
		Cluster: aws.String(clusetrName),
	}
	result, err := e.ListServices(listInput)
	if err != nil {
		return false, err
	}
	for _, v := range result.ServiceArns {
		s, err := parseArn(v)
		if err != nil {
			return false, err
		}
		if *s.Name == serviceName {
			return true, nil
		}
	}
	return false, nil
}

func parseArn(arn *string) (*Service, error) {
	splitStr := strings.Split(*arn, "service/")
	if len(splitStr) != 2 {
		return nil, errors.New("illegal arn string")
	}
	return &Service{&splitStr[1]}, nil
}
<|endoftext|>


<commit_before>package huton

import (
	"io"

	"github.com/golang/protobuf/proto"
	"github.com/hashicorp/raft"
	"github.com/jonbonazza/huton/lib/proto"
)

const (
	typeCacheExecute uint32 = iota
	typeLeaveCluster
)

func (i *instance) Apply(l *raft.Log) interface{} {
	var cmd huton_proto.Command
	if err := proto.Unmarshal(l.Data, &cmd); err != nil {
		return err
	}
	i.applyCommand(&cmd)
	return nil
}

func (i *instance) applyCommand(cmd *huton_proto.Command) {
	for j := 0; j <= i.raftApplicationRetries; j++ {
		switch *cmd.Type {
		case typeCacheExecute:
			var cacheBatchCmd huton_proto.CacheBatch
			if err := proto.Unmarshal(cmd.Body, &cacheBatchCmd); err != nil {
				continue
			}
			i.applyCacheBatch(&cacheBatchCmd)
		case typeLeaveCluster:
			i.applyLeaveCluster(string(cmd.Body))
		}
	}
}

func (i *instance) applyCacheBatch(cmd *huton_proto.CacheBatch) {
	for j := 0; j <= i.raftApplicationRetries; j++ {
		c := i.Cache(*cmd.CacheName)
		seg := &segment{
			buf:  cmd.Buf,
			meta: cmd.Meta,
		}
		err := c.(*cache).executeSegment(seg)
		if err != nil {
			break
		}
		i.logger.Printf("[ERR] failed to execute batch: %v", err)
		if j < i.raftApplicationRetries {
			i.logger.Println("[INFO] retrying batch execution...")
		}
	}
}

func (i *instance) applyLeaveCluster(name string) {
	if i.IsLeader() {
		if err := i.raft.RemoveServer(raft.ServerID(name), 0, 0).Error(); err != nil {
			i.logger.Printf("[WARN] failed to remove peer: %v", err)
		}
	}
}

func (i *instance) Snapshot() (raft.FSMSnapshot, error) {
	return &fsmSnapshot{}, nil
}

func (i *instance) Restore(rc io.ReadCloser) error {
	return nil
}

type fsmSnapshot struct{}

func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
	return nil
}

func (f *fsmSnapshot) Release() {
}
<commit_msg>Better handle unsupported snapshots<commit_after>package huton

import (
	"errors"
	"io"

	"github.com/golang/protobuf/proto"
	"github.com/hashicorp/raft"
	"github.com/jonbonazza/huton/lib/proto"
)

const (
	typeCacheExecute uint32 = iota
	typeLeaveCluster
)

var (
	errSnapshotsNotSupported = errors.New("snapshots not supported")
)

func (i *instance) Apply(l *raft.Log) interface{} {
	var cmd huton_proto.Command
	if err := proto.Unmarshal(l.Data, &cmd); err != nil {
		return err
	}
	i.applyCommand(&cmd)
	return nil
}

func (i *instance) applyCommand(cmd *huton_proto.Command) {
	for j := 0; j <= i.raftApplicationRetries; j++ {
		switch *cmd.Type {
		case typeCacheExecute:
			var cacheBatchCmd huton_proto.CacheBatch
			if err := proto.Unmarshal(cmd.Body, &cacheBatchCmd); err != nil {
				continue
			}
			i.applyCacheBatch(&cacheBatchCmd)
		case typeLeaveCluster:
			i.applyLeaveCluster(string(cmd.Body))
		}
	}
}

func (i *instance) applyCacheBatch(cmd *huton_proto.CacheBatch) {
	for j := 0; j <= i.raftApplicationRetries; j++ {
		c := i.Cache(*cmd.CacheName)
		seg := &segment{
			buf:  cmd.Buf,
			meta: cmd.Meta,
		}
		err := c.(*cache).executeSegment(seg)
		if err != nil {
			break
		}
		i.logger.Printf("[ERR] failed to execute batch: %v", err)
		if j < i.raftApplicationRetries {
			i.logger.Println("[INFO] retrying batch execution...")
		}
	}
}

func (i *instance) applyLeaveCluster(name string) {
	if i.IsLeader() {
		if err := i.raft.RemoveServer(raft.ServerID(name), 0, 0).Error(); err != nil {
			i.logger.Printf("[WARN] failed to remove peer: %v", err)
		}
	}
}

func (i *instance) Snapshot() (raft.FSMSnapshot, error) {
	return nil, errSnapshotsNotSupported
}

func (i *instance) Restore(rc io.ReadCloser) error {
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gxui

import (
	"github.com/google/gxui/math"
)

type Viewport interface {
	// SizeDips returns the size of the viewport in device-independent pixels.
	// The ratio of pixels to DIPs is based on the screen density and scale
	// adjustments made with the SetScale method.
	SizeDips() math.Size

	// SetSizeDips sets the size of the viewport in device-independent pixels.
	// The ratio of pixels to DIPs is based on the screen density and scale
	// adjustments made with the SetScale method.
	SetSizeDips(math.Size)

	// SizePixels returns the size of the viewport in pixels.
	SizePixels() math.Size

	// Scale returns the display scaling for this viewport.
	// A scale of 1 is unscaled, 2 is twice the regular scaling.
	Scale() float32

	// SetScale alters the display scaling for this viewport.
	// A scale of 1 is unscaled, 2 is twice the regular scaling.
	SetScale(float32)

	// Fullscreen returns true if the viewport was created full-screen.
	Fullscreen() bool

	// Title returns the title of the window.
	// This is usually the text displayed at the top of the window.
	Title() string

	// SetTitle changes the title of the window.
	SetTitle(string)

	// Pos returns position of the window.
	Pos() (int, int)

	// SetPos changes position of the window.
	SetPos(int, int)

	// Show makes the window visible.
	Show()

	// Hide makes the window invisible.
	Hide()

	// Close destroys the window.
	// Once the window is closed, no further calls should be made to it.
	Close()

	// SetCanvas changes the displayed content of the viewport to the specified
	// Canvas. As canvases are immutable once completed, every visual update of a
	// viewport will require a call to SetCanvas.
	SetCanvas(Canvas)

	// OnClose subscribes f to be called when the viewport closes.
	OnClose(f func()) EventSubscription

	// OnResize subscribes f to be called whenever the viewport changes size.
	OnResize(f func()) EventSubscription

	// OnMouseMove subscribes f to be called whenever the mouse cursor moves over
	// the viewport.
	OnMouseMove(f func(MouseEvent)) EventSubscription

	// OnMouseEnter subscribes f to be called whenever the mouse cursor enters the
	// viewport.
	OnMouseEnter(f func(MouseEvent)) EventSubscription

	// OnMouseEnter subscribes f to be called whenever the mouse cursor leaves the
	// viewport.
	OnMouseExit(f func(MouseEvent)) EventSubscription

	// OnMouseDown subscribes f to be called whenever a mouse button is pressed
	// while the cursor is inside the viewport.
	OnMouseDown(f func(MouseEvent)) EventSubscription

	// OnMouseUp subscribes f to be called whenever a mouse button is released
	// while the cursor is inside the viewport.
	OnMouseUp(f func(MouseEvent)) EventSubscription

	// OnMouseScroll subscribes f to be called whenever the mouse scroll wheel
	// turns while the cursor is inside the viewport.
	OnMouseScroll(f func(MouseEvent)) EventSubscription

	// OnKeyDown subscribes f to be called whenever a keyboard key is pressed
	// while the viewport has focus.
	OnKeyDown(f func(KeyboardEvent)) EventSubscription

	// OnKeyUp subscribes f to be called whenever a keyboard key is released
	// while the viewport has focus.
	OnKeyUp(f func(KeyboardEvent)) EventSubscription

	// OnKeyRepeat subscribes f to be called whenever a keyboard key-repeat event
	// is raised while the viewport has focus.
	OnKeyRepeat(f func(KeyboardEvent)) EventSubscription

	// OnKeyStroke subscribes f to be called whenever a keyboard key-stroke event
	// is raised while the viewport has focus.
	OnKeyStroke(f func(KeyStrokeEvent)) EventSubscription
}
<commit_msg>fix: gofmted out empty line<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gxui

import (
	"github.com/google/gxui/math"
)

type Viewport interface {
	// SizeDips returns the size of the viewport in device-independent pixels.
	// The ratio of pixels to DIPs is based on the screen density and scale
	// adjustments made with the SetScale method.
	SizeDips() math.Size

	// SetSizeDips sets the size of the viewport in device-independent pixels.
	// The ratio of pixels to DIPs is based on the screen density and scale
	// adjustments made with the SetScale method.
	SetSizeDips(math.Size)

	// SizePixels returns the size of the viewport in pixels.
	SizePixels() math.Size

	// Scale returns the display scaling for this viewport.
	// A scale of 1 is unscaled, 2 is twice the regular scaling.
	Scale() float32

	// SetScale alters the display scaling for this viewport.
	// A scale of 1 is unscaled, 2 is twice the regular scaling.
	SetScale(float32)

	// Fullscreen returns true if the viewport was created full-screen.
	Fullscreen() bool

	// Title returns the title of the window.
	// This is usually the text displayed at the top of the window.
	Title() string

	// SetTitle changes the title of the window.
	SetTitle(string)

	// Pos returns position of the window.
	Pos() (int, int)

	// SetPos changes position of the window.
	SetPos(int, int)

	// Show makes the window visible.
	Show()

	// Hide makes the window invisible.
	Hide()

	// Close destroys the window.
	// Once the window is closed, no further calls should be made to it.
	Close()

	// SetCanvas changes the displayed content of the viewport to the specified
	// Canvas. As canvases are immutable once completed, every visual update of a
	// viewport will require a call to SetCanvas.
	SetCanvas(Canvas)

	// OnClose subscribes f to be called when the viewport closes.
	OnClose(f func()) EventSubscription

	// OnResize subscribes f to be called whenever the viewport changes size.
	OnResize(f func()) EventSubscription

	// OnMouseMove subscribes f to be called whenever the mouse cursor moves over
	// the viewport.
	OnMouseMove(f func(MouseEvent)) EventSubscription

	// OnMouseEnter subscribes f to be called whenever the mouse cursor enters the
	// viewport.
	OnMouseEnter(f func(MouseEvent)) EventSubscription

	// OnMouseEnter subscribes f to be called whenever the mouse cursor leaves the
	// viewport.
	OnMouseExit(f func(MouseEvent)) EventSubscription

	// OnMouseDown subscribes f to be called whenever a mouse button is pressed
	// while the cursor is inside the viewport.
	OnMouseDown(f func(MouseEvent)) EventSubscription

	// OnMouseUp subscribes f to be called whenever a mouse button is released
	// while the cursor is inside the viewport.
	OnMouseUp(f func(MouseEvent)) EventSubscription

	// OnMouseScroll subscribes f to be called whenever the mouse scroll wheel
	// turns while the cursor is inside the viewport.
	OnMouseScroll(f func(MouseEvent)) EventSubscription

	// OnKeyDown subscribes f to be called whenever a keyboard key is pressed
	// while the viewport has focus.
	OnKeyDown(f func(KeyboardEvent)) EventSubscription

	// OnKeyUp subscribes f to be called whenever a keyboard key is released
	// while the viewport has focus.
	OnKeyUp(f func(KeyboardEvent)) EventSubscription

	// OnKeyRepeat subscribes f to be called whenever a keyboard key-repeat event
	// is raised while the viewport has focus.
	OnKeyRepeat(f func(KeyboardEvent)) EventSubscription

	// OnKeyStroke subscribes f to be called whenever a keyboard key-stroke event
	// is raised while the viewport has focus.
	OnKeyStroke(f func(KeyStrokeEvent)) EventSubscription
}
<|endoftext|>


<commit_before>package integration_test

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"syscall"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Emitting progress", func() {
	Describe("progress reports", func() {
		BeforeEach(func() {
			fm.MountFixture("progress_reporter")
		})

		It("emits progress when a singal is sent and when tests take too long", func() {
			session := startGinkgo(fm.PathTo("progress_reporter"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color")
			Eventually(session).Should(gbytes.Say(`READY `))
			buf := make([]byte, 128)
			_, err := session.Out.Read(buf)
			Ω(err).ShouldNot(HaveOccurred())
			pid, err := strconv.Atoi(regexp.MustCompile(`\d*`).FindString(string(buf)))
			Ω(err).ShouldNot(HaveOccurred())

			syscall.Kill(pid, syscall.SIGUSR1)
			Eventually(session).Should(gbytes.Say(`can track on demand \(Spec Runtime:`))
			Eventually(session).Should(gbytes.Say(`In \[It\] \(Node Runtime:`))
			Eventually(session).Should(gbytes.Say(`\[By Step\] Step B \(Step Runtime:`))
			Eventually(session).Should(gbytes.Say(`|\s*fmt\.Println\("READY"\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time\.Sleep\(time\.Second\)`))

			//first poll
			Eventually(session).Should(gbytes.Say(`--poll-progress-after tracks things that take too long \(Spec Runtime: 1\.5\d*s\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time.Sleep\(2 \* time\.Second\)`))

			//second poll
			Eventually(session).Should(gbytes.Say(`--poll-progress-after tracks things that take too long \(Spec Runtime: 1\.7\d*s\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time.Sleep\(2 \* time\.Second\)`))

			//decorator poll
			Eventually(session).Should(gbytes.Say(`decorator tracks things that take too long \(Spec Runtime: 5[\.\d]*ms\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time\.Sleep\(1 \* time\.Second\)`))

			Eventually(session).Should(gexec.Exit(0))
		})

		FIt("allows the user to specify a source-root to find source code files", func() {
			// first we build the test with -gcflags=all=-trimpath=<PATH TO SPEC> to ensure
			// that stack traces do not contain absolute paths
			path, err := filepath.Abs(fm.PathTo("progress_reporter"))
			Ω(err).ShouldNot(HaveOccurred())
			fmt.Println(path)
			session := startGinkgo(fm.PathTo("progress_reporter"), "build", `-gcflags=-trimpath=`+path+``)
			Eventually(session).Should(gexec.Exit(0))

			// now we move the compiled test binary to a separate directory
			fm.MkEmpty("progress_reporter/suite")
			os.Rename(fm.PathTo("progress_reporter", "progress_reporter.test"), fm.PathTo("progress_reporter", "suite", "progress_reporter.test"))

			//and we run and confirm that we don't see the expected source code
			session = startGinkgo(fm.PathTo("progress_reporter", "suite"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color", "-label-filter=one-second", "./progress_reporter.test")
			Eventually(session).Should(gexec.Exit(0))
			Ω(session).ShouldNot(gbytes.Say(`>\s*time.Sleep\(1 \* time\.Second\)`))

			// now we run, but configured with source-root and see that we have the file
			// note that multipel source-roots can be passed in
			session = startGinkgo(fm.PathTo("progress_reporter", "suite"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color", "-label-filter=one-second", "--source-root=/tmp", "--source-root="+path, "./progress_reporter.test")
			Eventually(session).Should(gbytes.Say(`>\s*time\.Sleep\(1 \* time\.Second\)`))
			Eventually(session).Should(gexec.Exit(0))
		})

		It("emits progress immediately and includes process information when running in parallel", func() {
			session := startGinkgo(fm.PathTo("progress_reporter"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color", "-procs=2", "-label-filter=parallel")
			Eventually(session).Should(gexec.Exit(0))

			Eventually(session.Out.Contents()).Should(ContainSubstring(`Progress Report for Ginkgo Process #1`))
			Eventually(session.Out.Contents()).Should(ContainSubstring(`Progress Report for Ginkgo Process #2`))

		})

	})

	Describe("the --progress flag", func() {
		var session *gexec.Session
		var args []string
		BeforeEach(func() {
			args = []string{"--no-color"}
			fm.MountFixture("progress")
		})

		JustBeforeEach(func() {
			session = startGinkgo(fm.PathTo("progress"), args...)
			Eventually(session).Should(gexec.Exit(0))
		})

		Context("with the -progress flag, but no -v flag", func() {
			BeforeEach(func() {
				args = append(args, "-progress")
			})

			It("should not emit progress", func() {
				Ω(session).ShouldNot(gbytes.Say("[bB]efore"))
			})
		})

		Context("with the -v flag", func() {
			BeforeEach(func() {
				args = append(args, "-v")
			})

			It("should not emit progress", func() {
				Ω(session).ShouldNot(gbytes.Say(`\[BeforeEach\]`))
				Ω(session).Should(gbytes.Say(`>outer before<`))
			})
		})

		Context("with the -progress flag and the -v flag", func() {
			BeforeEach(func() {
				args = append(args, "-progress", "-v")
			})

			It("should emit progress (by writing to the GinkgoWriter)", func() {
				// First spec

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer before<`))

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] Inner Context`))
				Ω(session).Should(gbytes.Say(`>inner before<`))

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] when Inner When`))
				Ω(session).Should(gbytes.Say(`>inner before<`))

				Ω(session).Should(gbytes.Say(`\[JustBeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer just before<`))

				Ω(session).Should(gbytes.Say(`\[JustBeforeEach\] Inner Context`))
				Ω(session).Should(gbytes.Say(`>inner just before<`))

				Ω(session).Should(gbytes.Say(`\[It\] should emit progress as it goes`))
				Ω(session).Should(gbytes.Say(`>it<`))

				Ω(session).Should(gbytes.Say(`\[AfterEach\] Inner Context`))
				Ω(session).Should(gbytes.Say(`>inner after<`))

				Ω(session).Should(gbytes.Say(`\[AfterEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer after<`))

				// Second spec

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer before<`))

				Ω(session).Should(gbytes.Say(`\[JustBeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer just before<`))

				Ω(session).Should(gbytes.Say(`\[It\] should emit progress as it goes`))
				Ω(session).Should(gbytes.Say(`>specify<`))

				Ω(session).Should(gbytes.Say(`\[AfterEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer after<`))
			})
		})
	})
})
<commit_msg>Fixed Github Actions integration spec<commit_after>package integration_test

import (
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"syscall"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Emitting progress", func() {
	Describe("progress reports", func() {
		BeforeEach(func() {
			fm.MountFixture("progress_reporter")
		})

		It("emits progress when a singal is sent and when tests take too long", func() {
			session := startGinkgo(fm.PathTo("progress_reporter"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color")
			Eventually(session).Should(gbytes.Say(`READY `))
			buf := make([]byte, 128)
			_, err := session.Out.Read(buf)
			Ω(err).ShouldNot(HaveOccurred())
			pid, err := strconv.Atoi(regexp.MustCompile(`\d*`).FindString(string(buf)))
			Ω(err).ShouldNot(HaveOccurred())

			syscall.Kill(pid, syscall.SIGUSR1)
			Eventually(session).Should(gbytes.Say(`can track on demand \(Spec Runtime:`))
			Eventually(session).Should(gbytes.Say(`In \[It\] \(Node Runtime:`))
			Eventually(session).Should(gbytes.Say(`\[By Step\] Step B \(Step Runtime:`))
			Eventually(session).Should(gbytes.Say(`|\s*fmt\.Println\("READY"\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time\.Sleep\(time\.Second\)`))

			//first poll
			Eventually(session).Should(gbytes.Say(`--poll-progress-after tracks things that take too long \(Spec Runtime: 1\.5\d*s\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time.Sleep\(2 \* time\.Second\)`))

			//second poll
			Eventually(session).Should(gbytes.Say(`--poll-progress-after tracks things that take too long \(Spec Runtime: 1\.7\d*s\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time.Sleep\(2 \* time\.Second\)`))

			//decorator poll
			Eventually(session).Should(gbytes.Say(`decorator tracks things that take too long \(Spec Runtime: 5[\.\d]*ms\)`))
			Eventually(session).Should(gbytes.Say(`>\s*time\.Sleep\(1 \* time\.Second\)`))

			Eventually(session).Should(gexec.Exit(0))
		})

		It("allows the user to specify a source-root to find source code files", func() {
			// first we build the test with -gcflags=all=-trimpath=<PATH TO SPEC> to ensure
			// that stack traces do not contain absolute paths
			path, err := filepath.Abs(fm.PathTo("progress_reporter"))
			Ω(err).ShouldNot(HaveOccurred())
			session := startGinkgo(fm.PathTo("progress_reporter"), "build", `-gcflags=-trimpath=`+path+``)
			Eventually(session).Should(gexec.Exit(0))

			// now we move the compiled test binary to a separate directory
			fm.MkEmpty("progress_reporter/suite")
			os.Rename(fm.PathTo("progress_reporter", "progress_reporter.test"), fm.PathTo("progress_reporter", "suite", "progress_reporter.test"))

			//and we run and confirm that we don't see the expected source code
			session = startGinkgo(fm.PathTo("progress_reporter", "suite"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color", "-label-filter=one-second", "./progress_reporter.test")
			Eventually(session).Should(gexec.Exit(0))
			Ω(session).ShouldNot(gbytes.Say(`>\s*time.Sleep\(1 \* time\.Second\)`))

			// now we run, but configured with source-root and see that we have the file
			// note that multipel source-roots can be passed in
			session = startGinkgo(fm.PathTo("progress_reporter", "suite"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color", "-label-filter=one-second", "--source-root=/tmp", "--source-root="+path, "./progress_reporter.test")
			Eventually(session).Should(gbytes.Say(`>\s*time\.Sleep\(1 \* time\.Second\)`))
			Eventually(session).Should(gexec.Exit(0))
		})

		It("emits progress immediately and includes process information when running in parallel", func() {
			session := startGinkgo(fm.PathTo("progress_reporter"), "--poll-progress-after=1500ms", "--poll-progress-interval=200ms", "--no-color", "-procs=2", "-label-filter=parallel")
			Eventually(session).Should(gexec.Exit(0))

			Eventually(session.Out.Contents()).Should(ContainSubstring(`Progress Report for Ginkgo Process #1`))
			Eventually(session.Out.Contents()).Should(ContainSubstring(`Progress Report for Ginkgo Process #2`))

		})

	})

	Describe("the --progress flag", func() {
		var session *gexec.Session
		var args []string
		BeforeEach(func() {
			args = []string{"--no-color"}
			fm.MountFixture("progress")
		})

		JustBeforeEach(func() {
			session = startGinkgo(fm.PathTo("progress"), args...)
			Eventually(session).Should(gexec.Exit(0))
		})

		Context("with the -progress flag, but no -v flag", func() {
			BeforeEach(func() {
				args = append(args, "-progress")
			})

			It("should not emit progress", func() {
				Ω(session).ShouldNot(gbytes.Say("[bB]efore"))
			})
		})

		Context("with the -v flag", func() {
			BeforeEach(func() {
				args = append(args, "-v")
			})

			It("should not emit progress", func() {
				Ω(session).ShouldNot(gbytes.Say(`\[BeforeEach\]`))
				Ω(session).Should(gbytes.Say(`>outer before<`))
			})
		})

		Context("with the -progress flag and the -v flag", func() {
			BeforeEach(func() {
				args = append(args, "-progress", "-v")
			})

			It("should emit progress (by writing to the GinkgoWriter)", func() {
				// First spec

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer before<`))

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] Inner Context`))
				Ω(session).Should(gbytes.Say(`>inner before<`))

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] when Inner When`))
				Ω(session).Should(gbytes.Say(`>inner before<`))

				Ω(session).Should(gbytes.Say(`\[JustBeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer just before<`))

				Ω(session).Should(gbytes.Say(`\[JustBeforeEach\] Inner Context`))
				Ω(session).Should(gbytes.Say(`>inner just before<`))

				Ω(session).Should(gbytes.Say(`\[It\] should emit progress as it goes`))
				Ω(session).Should(gbytes.Say(`>it<`))

				Ω(session).Should(gbytes.Say(`\[AfterEach\] Inner Context`))
				Ω(session).Should(gbytes.Say(`>inner after<`))

				Ω(session).Should(gbytes.Say(`\[AfterEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer after<`))

				// Second spec

				Ω(session).Should(gbytes.Say(`\[BeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer before<`))

				Ω(session).Should(gbytes.Say(`\[JustBeforeEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer just before<`))

				Ω(session).Should(gbytes.Say(`\[It\] should emit progress as it goes`))
				Ω(session).Should(gbytes.Say(`>specify<`))

				Ω(session).Should(gbytes.Say(`\[AfterEach\] ProgressFixture`))
				Ω(session).Should(gbytes.Say(`>outer after<`))
			})
		})
	})
})
<|endoftext|>


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

// +build !gccgo

package vsphere

import (
	"github.com/juju/juju/environs"
)

var (
	Provider environs.EnvironProvider = providerInstance
)

func init() {
	environs.RegisterProvider(providerType, providerInstance)
}

func ExposeEnvFakeClient(env *environ) *fakeClient {
	return env.client.connection.RoundTripper.(*fakeClient)
}

var _ environs.Environ = (*environ)(nil)
<commit_msg>Drop registering the provider during tests.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build !gccgo

package vsphere

import (
	"github.com/juju/juju/environs"
)

var (
	Provider environs.EnvironProvider = providerInstance
)

func ExposeEnvFakeClient(env *environ) *fakeClient {
	return env.client.connection.RoundTripper.(*fakeClient)
}

var _ environs.Environ = (*environ)(nil)
<|endoftext|>


<commit_before>package incata

import (
	"database/sql/driver"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/twinj/uuid"
)

type AnyType struct{}

// Match satisfies sqlmock.Argument interface
func (a AnyType) Match(v driver.Value) bool {
	return true
}

func TestSqlReaderRead(t *testing.T) {

    t.Skip("Not working yet!!!")

	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	storage, _ := NewDbFinalized(db, MSSQL)

	var sourceID = uuid.NewV4()

	mock.ExpectQuery("SELECT Id ,SourceId ,Created ,EventType ,Version ,Payload FROM Event WHERE SourceId =").WithArgs(AnyTime{})

	marshaller := NewJSONMarshaller()
	reader := NewSQLReader(storage, marshaller)

	reader.Read(sourceID)

	if err = mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("there were unfulfilled expections: %s", err)
	}
}
<commit_msg>regex matching try<commit_after>package incata

import (
	"database/sql/driver"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/twinj/uuid"
)

type AnyType struct{}

// Match satisfies sqlmock.Argument interface
func (a AnyType) Match(v driver.Value) bool {
	return true
}

func TestSqlReaderRead(t *testing.T) {

    t.Skip("Not working yet!!!")

	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	storage, _ := NewDbFinalized(db, MSSQL)

	var sourceID = uuid.NewV4()

	mock.ExpectQuery("^SELECT (.*) FROM Event WHERE SourceId = (.*)$").WithArgs(AnyTime{})

	marshaller := NewJSONMarshaller()
	reader := NewSQLReader(storage, marshaller)

	reader.Read(sourceID)

	if err = mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("there were unfulfilled expections: %s", err)
	}
}
<|endoftext|>


<commit_before>// package threedsecure provides the /3d_secure APIs
package threedsecure

import (
	"strconv"

	"github.com/stripe/stripe-go"
)

type Client struct {
	B   stripe.Backend
	Key string
}

func New(params *stripe.ThreeDSecureParams) (*stripe.ThreeDSecure, error) {
	return getC().New(params)
}

func (c Client) New(params *stripe.ThreeDSecureParams) (*stripe.ThreeDSecure, error) {
	body := &stripe.RequestValues{}
	body.Add("amount", strconv.FormatUint(params.Amount, 10))
	body.Add("currency", string(params.Currency))
	body.Add("card", params.Card)
	body.Add("return_url", params.ReturnURL)

	params.AppendTo(body)

	if len(params.Customer) > 0 {
		body.Add("customer", params.Customer)
	}

	tds := &stripe.ThreeDSecure{}
	err := c.B.Call("POST", "/3d_secure", c.Key, body, &params.Params, tds)
	return tds, err
}

func getC() Client {
	return Client{stripe.GetBackend(stripe.APIBackend), stripe.Key}
}
<commit_msg>address PR comments<commit_after>// package threedsecure provides the /3d_secure APIs
package threedsecure

import (
	"strconv"

	"github.com/stripe/stripe-go"
)

type Client struct {
	B   stripe.Backend
	Key string
}

func New(params *stripe.ThreeDSecureParams) (*stripe.ThreeDSecure, error) {
	return getC().New(params)
}

func (c Client) New(params *stripe.ThreeDSecureParams) (*stripe.ThreeDSecure, error) {
	body := &stripe.RequestValues{}
	body.Add("amount", strconv.FormatUint(params.Amount, 10))
	body.Add("card", params.Card)
	body.Add("currency", string(params.Currency))
	body.Add("return_url", params.ReturnURL)

	if len(params.Customer) > 0 {
		body.Add("customer", params.Customer)
	}

	params.AppendTo(body)

	tds := &stripe.ThreeDSecure{}
	err := c.B.Call("POST", "/3d_secure", c.Key, body, &params.Params, tds)
	return tds, err
}

func getC() Client {
	return Client{stripe.GetBackend(stripe.APIBackend), stripe.Key}
}
<|endoftext|>


<commit_before>package appmanager

import (
	"encoding/json"

	log "github.com/F5Networks/k8s-bigip-ctlr/pkg/vlogger"
)

type as3Template string
type as3Declaration string

type serviceName string
type appName string
type tenantName string

type pool []Member
type tenant map[appName][]serviceName
type as3Object map[tenantName]tenant

// Takes an AS3 Template and perform service discovery with Kubernetes to generate AS3 Declaration
func (appMgr *Manager) processUserDefinedAS3(template as3Template) {
	// TODO: Implement Me

}

// getAS3ObjectFromTemplate gets an AS3 template as a input parameter.
// It parses AS3 template, constructs an as3Object and returns it.
func (appMgr *Manager) getAS3ObjectFromTemplate(
	template as3Template,
) (as3Object, bool) {
	var tmpl interface{}
	err := json.Unmarshal([]byte(template), &tmpl)
	if err != nil {
		log.Errorf("JSON unmarshal failed: %v\n", err)
		return nil, false
	}

	as3 := make(as3Object)
	// extract as3 decleration from template
	dclr := (tmpl.(map[string]interface{}))["decleration"]

	// Loop over all the tenants
	for tn, t := range dclr.(map[string]interface{}) {
		// Filter out non-json values
		if _, ok := t.(map[string]interface{}); !ok {
			continue
		}

		as3[tenantName(tn)] = make(tenant, 0)
		// Loop over all the services in a tenant
		for an, a := range t.(map[string]interface{}) {
			// Filter out non-json values
			if _, ok := a.(map[string]interface{}); !ok {
				continue
			}

			as3[tenantName(tn)][appName(an)] = []serviceName{}
			// Loop over all the json objects in an application
			for sn, v := range a.(map[string]interface{}) {
				// Filter out non-json values
				if _, ok := v.(map[string]interface{}); !ok {
					continue
				}

				// filter out empty json objects and pool objects
				if cl := getClass(v); cl == "" || cl == "Pool" {
					continue
				}
				//Update the list of services under corresponding application
				as3[tenantName(tn)][appName(an)] = append(
					as3[tenantName(tn)][appName(an)],
					serviceName(sn),
				)
			}
			if len(as3[tenantName(tn)][appName(an)]) == 0 {
				log.Debugf("No services declared for application: %s,"+
					" tenant: %s\n", an, tn)
			}
		}
		if len(as3[tenantName(tn)]) == 0 {
			log.Debugf("No applications declared for tenant: %s\n", tn)
		}
	}
	if len(as3) == 0 {
		log.Error("No tenants declared in AS3 template")
		return as3, false
	}
	return as3, true
}

func getClass(obj interface{}) string {
	cfg := obj.(map[string]interface{})
	cl, ok := cfg["class"].(string)
	if !ok {
		log.Debugf("No class attribute found")
		return ""
	}
	return cl
}

// Story 3
// Discover Endpoints for an application. Returns a pool
func (appMgr *Manager) getEndpointsForAS3Service(tenant tenantName, app appName, as3Svc serviceName) pool {
	return pool{}
}

// Returns a pool of IP address.
func (appMgr *Manager) getFakeEndpointsForAS3Service(tenant tenantName, app appName, as3Svc serviceName) pool {
	return []Member{
		{"1.1.1.1", 80, ""},
		{"2.2.2.2", 80, ""},
		{"3.3.3.3", 80, ""},
	}
}

// Story 4
// Takes AS3 template and AS3 Object and produce AS3 Declaration
func buildAS3Declaration(obj as3Object, template as3Template) as3Declaration {
	decalaration := ""
	return as3Declaration(decalaration)
}

//Story 5
// Takes AS3 Declaration and posting it to BigIP
func (appMgr *Manager) postAS3Declaration(declaration as3Declaration) {
}
<commit_msg>Builds AS3 declaration from AS3 template<commit_after>package appmanager

import (
	"encoding/json"

	log "github.com/F5Networks/k8s-bigip-ctlr/pkg/vlogger"
)

type as3Template string
type as3Declaration string

type serviceName string
type appName string
type tenantName string

type pool []Member
type tenant map[appName][]serviceName
type as3Object map[tenantName]tenant

// Takes an AS3 Template and perform service discovery with Kubernetes to generate AS3 Declaration
func (appMgr *Manager) processUserDefinedAS3(template as3Template) {
	// TODO: Implement Me

}

// getAS3ObjectFromTemplate gets an AS3 template as a input parameter.
// It parses AS3 template, constructs an as3Object and returns it.
func (appMgr *Manager) getAS3ObjectFromTemplate(
	template as3Template,
) (as3Object, bool) {
	var tmpl interface{}
	err := json.Unmarshal([]byte(template), &tmpl)
	if err != nil {
		log.Errorf("JSON unmarshal failed: %v\n", err)
		return nil, false
	}

	as3 := make(as3Object)
	// extract as3 decleration from template
	dclr := (tmpl.(map[string]interface{}))["decleration"]

	// Loop over all the tenants
	for tn, t := range dclr.(map[string]interface{}) {
		// Filter out non-json values
		if _, ok := t.(map[string]interface{}); !ok {
			continue
		}

		as3[tenantName(tn)] = make(tenant, 0)
		// Loop over all the services in a tenant
		for an, a := range t.(map[string]interface{}) {
			// Filter out non-json values
			if _, ok := a.(map[string]interface{}); !ok {
				continue
			}

			as3[tenantName(tn)][appName(an)] = []serviceName{}
			// Loop over all the json objects in an application
			for sn, v := range a.(map[string]interface{}) {
				// Filter out non-json values
				if _, ok := v.(map[string]interface{}); !ok {
					continue
				}

				// filter out empty json objects and pool objects
				if cl := getClass(v); cl == "" || cl == "Pool" {
					continue
				}
				//Update the list of services under corresponding application
				as3[tenantName(tn)][appName(an)] = append(
					as3[tenantName(tn)][appName(an)],
					serviceName(sn),
				)
			}
			if len(as3[tenantName(tn)][appName(an)]) == 0 {
				log.Debugf("No services declared for application: %s,"+
					" tenant: %s\n", an, tn)
			}
		}
		if len(as3[tenantName(tn)]) == 0 {
			log.Debugf("No applications declared for tenant: %s\n", tn)
		}
	}
	if len(as3) == 0 {
		log.Error("No tenants declared in AS3 template")
		return as3, false
	}
	return as3, true
}

func getClass(obj interface{}) string {
	cfg := obj.(map[string]interface{})
	cl, ok := cfg["class"].(string)
	if !ok {
		log.Debugf("No class attribute found")
		return ""
	}
	return cl
}

// Story 3
// Discover Endpoints for an application. Returns a pool
func (appMgr *Manager) getEndpointsForAS3Service(tenant tenantName, app appName, as3Svc serviceName) pool {
	return pool{}
}

// Returns a pool of IP address.
func (appMgr *Manager) getFakeEndpointsForAS3Service(tenant tenantName, app appName, as3Svc serviceName) pool {
	return []Member{
		{"1.1.1.1", 80, ""},
		{"2.2.2.2", 80, ""},
		{"3.3.3.3", 80, ""},
	}
}

// Traverses through the AS3 JSON using the information passed from buildAS3Declaration,
// parses the AS3 JSON and populates it with pool members
func updatePoolMembers(tnt tenantName, app appName, svc serviceName, ips []string, port int32, templateJSON map[string]interface{}) map[string]interface{} {

	// Get the declaration object from AS3 Json
	dec := (templateJSON["declaration"]).(map[string]interface{})
	// Get the tenant object from AS3 Json
	tet := (dec[string(tnt)]).(map[string]interface{})

	// Get the poolname for the serviceName svc
	apps := (tet[string(app)].(map[string]interface{}))
	servicemain := (apps[string(svc)].(map[string]interface{}))
	poolname := (servicemain["pool"].(string))

	// Continue with the poolName and replace the as3 template with poolMembers
	toName := (tet[string(app)].(map[string]interface{}))
	pool := (toName[string(poolname)].(map[string]interface{}))
	poolmem := (((pool["members"]).([]interface{}))[0]).(map[string]interface{})

	// Replace pool member IP addresses
	poolmem["serverAddresses"] = ips
	// Replace port number
	poolmem["servicePort"] = port
	return templateJSON
}

// Takes AS3 template and AS3 Object and produce AS3 Declaration
func (appMgr *Manager) buildAS3Declaration(obj as3Object, template as3Template) as3Declaration {

	var tmp interface{}
	// unmarshall the template of type string to interface
	err := json.Unmarshal([]byte(template), &tmp)
	if nil != err {
		return ""
	}

	// convert tmp to map[string]interface{}, This conversion will help in traversing the as3 object
	templateJSON := tmp.(map[string]interface{})

	// traverse through the as3 object to fetch the list of services and get endpopints using the servicename
	log.Debugf("[as3_log] Started Parsing the AS3 Object")
	for tnt, apps := range obj {
		for app, svcs := range apps {
			for svc := range svcs {
				eps := appMgr.getEndpointsForAS3Service(tnt, app, svcs[svc])
				// Handle an empty value
				if len(eps) == 0 {
					continue
				}
				ips := make([]string, 0)
				for _, v := range eps {
					ips = append(ips, v.Address)
				}
				port := eps[0].Port
				log.Debugf("Updating AS3 Template for tenant '%s' app '%s' service '%s', ", tnt, app, svcs[svc])
				updatePoolMembers(tnt, app, svcs[svc], ips, port, templateJSON)
			}
		}
	}

	declaration, err := json.Marshal(templateJSON)
	if err != nil {
		log.Errorf("[as3_log] Issue marshalling AS3 Json")
	}
	log.Debugf("[as3_log] AS3 Template is populated with the pool members")
	log.Debugf("[as3_log] Printing AS3 Template ...")
	log.Debugf("%s", declaration)

	return as3Declaration(declaration)

}

//Story 5
// Takes AS3 Declaration and posting it to BigIP
func (appMgr *Manager) postAS3Declaration(declaration as3Declaration) {
}
<|endoftext|>