func New(proxySignals <-chan os.Signal, runner ifrit.Runner) ifrit.Runner {
	return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		process := ifrit.Background(runner)
		<-process.Ready()
		close(ready)
		go forwardSignals(proxySignals, process)
		go forwardSignals(signals, process)
		return <-process.Wait()
	})
}
	return p, true
}

func (p PingChan) Run(sigChan <-chan os.Signal, ready chan<- struct{}) error {
	close(ready)
	select {
	case <-sigChan:
		return PingerExitedFromSignal
	case p <- Ping{}:
		return PingerExitedFromPing
	}
}

// NoReadyRunner exits without closing the ready chan
var NoReadyRunner = ifrit.RunFunc(func(sigChan <-chan os.Signal, ready chan<- struct{}) error {
	return NoReadyExitedNormally
})

var NoReadyExitedNormally = errors.New("no ready exited normally")

// SignalRecoder records all signals received, and exits on a set of signals.
type SignalRecoder struct {
	sync.RWMutex
	signals     []os.Signal
	exitSignals map[os.Signal]struct{}
}

func NewSignalRecorder(exitSignals ...os.Signal) *SignalRecoder {
	exitSignals = append(exitSignals, os.Kill, os.Interrupt)

	signalSet := map[os.Signal]struct{}{}
	Describe("Stop", func() {

		var runnerIndex int64
		var startOrder chan int64
		var stopOrder chan int64

		makeRunner := func(waitTime time.Duration) (ifrit.Runner, chan struct{}) {
			quickExit := make(chan struct{})
			return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
				index := atomic.AddInt64(&runnerIndex, 1)
				startOrder <- index
				close(ready)

				select {
				case <-quickExit:
				case <-signals:
				}
				time.Sleep(waitTime)
				stopOrder <- index

				return nil
			}), quickExit
		}

		BeforeEach(func() {
			startOrder = make(chan int64, 3)
			stopOrder = make(chan int64, 3)

			r1, _ := makeRunner(0)
			r2, _ := makeRunner(30 * time.Millisecond)
			r3, _ := makeRunner(50 * time.Millisecond)
	var (
		lifecycle        ifrit.Process
		fakeDeamonRunner func(signals <-chan os.Signal, ready chan<- struct{}) error
	)

	BeforeEach(func() {
		builder := main.Builder{
			RepoName:            "ubuntu",
			Tag:                 "latest",
			OutputFilename:      "/tmp/result/result.json",
			DockerDaemonTimeout: 300 * time.Millisecond,
			CacheDockerImage:    true,
		}

		lifecycle = ifrit.Background(grouper.NewParallel(os.Interrupt, grouper.Members{
			{"builder", ifrit.RunFunc(builder.Run)},
			{"fake_docker_daemon", ifrit.RunFunc(fakeDeamonRunner)},
		}))
	})

	AfterEach(func() {
		ginkgomon.Interrupt(lifecycle)
	})

	Context("when the daemon won't start", func() {
		fakeDeamonRunner = func(signals <-chan os.Signal, ready chan<- struct{}) error {
			close(ready)
			select {
			case signal := <-signals:
				return errors.New(signal.String())
			case <-time.After(1 * time.Second):
func main() {
	var insecureDockerRegistries registries
	var dockerRegistryIPs ips

	flagSet := flag.NewFlagSet("builder", flag.ExitOnError)

	dockerImageURL := flagSet.String(
		"dockerImageURL",
		"",
		"docker image uri in docker://[registry/][scope/]repository[#tag] format",
	)

	dockerRef := flagSet.String(
		"dockerRef",
		"",
		"docker image reference in standard docker string format",
	)

	outputFilename := flagSet.String(
		"outputMetadataJSONFilename",
		"/tmp/result/result.json",
		"filename in which to write the app metadata",
	)

	flagSet.Var(
		&insecureDockerRegistries,
		"insecureDockerRegistries",
		"insecure Docker Registry addresses (host:port)",
	)

	dockerDaemonExecutablePath := flagSet.String(
		"dockerDaemonExecutablePath",
		"/tmp/docker_app_lifecycle/docker",
		"path to the 'docker' executable",
	)

	cacheDockerImage := flagSet.Bool(
		"cacheDockerImage",
		false,
		"Caches Docker images to private docker registry",
	)

	flagSet.Var(
		&dockerRegistryIPs,
		"dockerRegistryIPs",
		"Docker Registry IPs",
	)

	dockerRegistryHost := flagSet.String(
		"dockerRegistryHost",
		"",
		"Docker Registry host",
	)

	dockerRegistryPort := flagSet.Int(
		"dockerRegistryPort",
		8080,
		"Docker Registry port",
	)

	dockerLoginServer := flagSet.String(
		"dockerLoginServer",
		registry.IndexServerAddress(),
		"Docker Login server address",
	)

	dockerUser := flagSet.String(
		"dockerUser",
		"",
		"User for pulling from docker registry",
	)

	dockerPassword := flagSet.String(
		"dockerPassword",
		"",
		"Password for pulling from docker registry",
	)

	dockerEmail := flagSet.String(
		"dockerEmail",
		"",
		"Email for pulling from docker registry",
	)

	if err := flagSet.Parse(os.Args[1:len(os.Args)]); err != nil {
		println(err.Error())
		os.Exit(1)
	}

	var repoName string
	var tag string
	if len(*dockerImageURL) > 0 {
		parts, err := url.Parse(*dockerImageURL)
		if err != nil {
			println("invalid dockerImageURL: " + *dockerImageURL)
			flagSet.PrintDefaults()
			os.Exit(1)
		}
		repoName, tag = helpers.ParseDockerURL(parts)
	} else if len(*dockerRef) > 0 {
		repoName, tag = helpers.ParseDockerRef(*dockerRef)
	} else {
		println("missing flag: dockerImageURL or dockerRef required")
		flagSet.PrintDefaults()
		os.Exit(1)
	}

	builder := Builder{
		RepoName:                   repoName,
		Tag:                        tag,
		OutputFilename:             *outputFilename,
		DockerDaemonExecutablePath: *dockerDaemonExecutablePath,
		InsecureDockerRegistries:   insecureDockerRegistries,
		DockerDaemonTimeout:        10 * time.Second,
		CacheDockerImage:           *cacheDockerImage,
		DockerRegistryIPs:          dockerRegistryIPs,
		DockerRegistryHost:         *dockerRegistryHost,
		DockerRegistryPort:         *dockerRegistryPort,
		DockerLoginServer:          *dockerLoginServer,
		DockerUser:                 *dockerUser,
		DockerPassword:             *dockerPassword,
		DockerEmail:                *dockerEmail,
	}

	members := grouper.Members{
		{"builder", ifrit.RunFunc(builder.Run)},
	}

	if *cacheDockerImage {
		if len(dockerRegistryIPs) == 0 {
			println("missing flag: dockerRegistryIPs required")
			os.Exit(1)
		}
		if len(*dockerRegistryHost) == 0 {
			println("missing flag: dockerRegistryHost required")
			os.Exit(1)
		}
		if strings.Contains(*dockerRegistryHost, ":") {
			println("invalid host format", *dockerRegistryHost)
			os.Exit(1)
		}
		if *dockerRegistryPort < 0 {
			println("negative port number", *dockerRegistryPort)
			os.Exit(1)
		}
		if *dockerRegistryPort > 65535 {
			println("port number too big", *dockerRegistryPort)
			os.Exit(1)
		}

		validateCredentials(*dockerLoginServer, *dockerUser, *dockerPassword, *dockerEmail)

		if _, err := os.Stat(*dockerDaemonExecutablePath); err != nil {
			println("docker daemon not found in", *dockerDaemonExecutablePath)
			os.Exit(1)
		}

		dockerDaemon := DockerDaemon{
			DockerDaemonPath:         *dockerDaemonExecutablePath,
			InsecureDockerRegistries: insecureDockerRegistries,
			DockerRegistryIPs:        dockerRegistryIPs,
			DockerRegistryHost:       *dockerRegistryHost,
		}
		members = append(members, grouper.Member{"docker_daemon", ifrit.RunFunc(dockerDaemon.Run)})
	}

	group := grouper.NewParallel(os.Interrupt, members)
	process := ifrit.Invoke(sigmon.New(group))

	fmt.Println("Staging process started ...")

	err := <-process.Wait()
	if err != nil {
		println("Staging process failed:", err.Error())
		os.Exit(2)
	}

	fmt.Println("Staging process finished")
}