<commit_before>package daemon // import "github.com/docker/docker/daemon"

import (
	"context"
	"time"

	"github.com/docker/docker/container"
	"github.com/docker/docker/errdefs"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// ContainerStop looks for the given container and stops it.
// In case the container fails to stop gracefully within a time duration
// specified by the timeout argument, in seconds, it is forcefully
// terminated (killed).
//
// If the timeout is nil, the container's StopTimeout value is used, if set,
// otherwise the engine default. A negative timeout value can be specified,
// meaning no timeout, i.e. no forceful termination is performed.
func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
	ctr, err := daemon.GetContainer(name)
	if err != nil {
		return err
	}
	if !ctr.IsRunning() {
		return containerNotModifiedError{}
	}
	err = daemon.containerStop(ctr, timeout)
	if err != nil {
		return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
	}
	return nil
}

// containerStop sends a stop signal, waits, sends a kill signal.
func (daemon *Daemon) containerStop(ctr *container.Container, seconds *int) error {
	// TODO propagate a context down to this function
	ctx := context.TODO()
	if !ctr.IsRunning() {
		return nil
	}

	var (
		stopSignal  = ctr.StopSignal()
		stopTimeout = ctr.StopTimeout()
	)
	if seconds != nil {
		stopTimeout = *seconds
	}

	var wait time.Duration
	if stopTimeout >= 0 {
		wait = time.Duration(stopTimeout) * time.Second
	}
	success := func() error {
		daemon.LogContainerEvent(ctr, "stop")
		return nil
	}

	// 1. Send a stop signal
	err := daemon.killPossiblyDeadProcess(ctr, stopSignal)
	if err != nil {
		wait = 2 * time.Second
	}

	var subCtx context.Context
	var cancel context.CancelFunc
	if stopTimeout >= 0 {
		subCtx, cancel = context.WithTimeout(ctx, wait)
	} else {
		subCtx, cancel = context.WithCancel(ctx)
	}
	defer cancel()

	if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil {
		// container did exit, so ignore any previous errors and return
		return success()
	}

	if err != nil {
		// the container has still not exited, and the kill function errored, so log the error here:
		logrus.WithError(err).WithField("container", ctr.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
	}
	if stopTimeout < 0 {
		// if the client requested that we never kill / wait forever, but container.Wait was still
		// interrupted (parent context cancelled, for example), we should propagate the signal failure
		return err
	}

	logrus.WithField("container", ctr.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)
	// Stop either failed or container didnt exit, so fallback to kill.
	if err := daemon.Kill(ctr); err != nil {
		// got a kill error, but give container 2 more seconds to exit just in case
		subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
		defer cancel()
		if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil {
			// container did exit, so ignore error and return
			return success()
		}
		logrus.WithError(err).WithField("container", ctr.ID).Error("Error killing the container")
		return err
	}

	return success()
}
<commit_msg>daemon: containerStop(): use a regular "defer" to log container event<commit_after>package daemon // import "github.com/docker/docker/daemon"

import (
	"context"
	"time"

	"github.com/docker/docker/container"
	"github.com/docker/docker/errdefs"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// ContainerStop looks for the given container and stops it.
// In case the container fails to stop gracefully within a time duration
// specified by the timeout argument, in seconds, it is forcefully
// terminated (killed).
//
// If the timeout is nil, the container's StopTimeout value is used, if set,
// otherwise the engine default. A negative timeout value can be specified,
// meaning no timeout, i.e. no forceful termination is performed.
func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
	ctr, err := daemon.GetContainer(name)
	if err != nil {
		return err
	}
	if !ctr.IsRunning() {
		return containerNotModifiedError{}
	}
	err = daemon.containerStop(ctr, timeout)
	if err != nil {
		return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
	}
	return nil
}

// containerStop sends a stop signal, waits, sends a kill signal.
func (daemon *Daemon) containerStop(ctr *container.Container, seconds *int) (retErr error) {
	// TODO propagate a context down to this function
	ctx := context.TODO()
	if !ctr.IsRunning() {
		return nil
	}

	var (
		stopSignal  = ctr.StopSignal()
		stopTimeout = ctr.StopTimeout()
	)
	if seconds != nil {
		stopTimeout = *seconds
	}

	var wait time.Duration
	if stopTimeout >= 0 {
		wait = time.Duration(stopTimeout) * time.Second
	}
	defer func() {
		if retErr == nil {
			daemon.LogContainerEvent(ctr, "stop")
		}
	}()

	// 1. Send a stop signal
	err := daemon.killPossiblyDeadProcess(ctr, stopSignal)
	if err != nil {
		wait = 2 * time.Second
	}

	var subCtx context.Context
	var cancel context.CancelFunc
	if stopTimeout >= 0 {
		subCtx, cancel = context.WithTimeout(ctx, wait)
	} else {
		subCtx, cancel = context.WithCancel(ctx)
	}
	defer cancel()

	if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil {
		// container did exit, so ignore any previous errors and return
		return nil
	}

	if err != nil {
		// the container has still not exited, and the kill function errored, so log the error here:
		logrus.WithError(err).WithField("container", ctr.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
	}
	if stopTimeout < 0 {
		// if the client requested that we never kill / wait forever, but container.Wait was still
		// interrupted (parent context cancelled, for example), we should propagate the signal failure
		return err
	}

	logrus.WithField("container", ctr.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)

	// Stop either failed or container didn't exit, so fallback to kill.
	if err := daemon.Kill(ctr); err != nil {
		// got a kill error, but give container 2 more seconds to exit just in case
		subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
		defer cancel()
		status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning)
		if status.Err() != nil {
			logrus.WithError(err).WithField("container", ctr.ID).Errorf("error killing container: %v", status.Err())
			return err
		}
		// container did exit, so ignore previous errors and continue
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 fatedier, fatedier@gmail.com
//
// 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 health

import (
	"context"
	"errors"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/fatedier/frp/utils/log"
)

var (
	ErrHealthCheckType = errors.New("error health check type")
)

type HealthCheckMonitor struct {
	checkType      string
	interval       time.Duration
	timeout        time.Duration
	maxFailedTimes int

	// For tcp
	addr string

	// For http
	url string

	failedTimes    uint64
	statusOK       bool
	statusNormalFn func()
	statusFailedFn func()

	ctx    context.Context
	cancel context.CancelFunc

	l log.Logger
}

func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string,
	statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor {

	if intervalS <= 0 {
		intervalS = 10
	}
	if timeoutS <= 0 {
		timeoutS = 3
	}
	if maxFailedTimes <= 0 {
		maxFailedTimes = 1
	}
	ctx, cancel := context.WithCancel(context.Background())
	return &HealthCheckMonitor{
		checkType:      checkType,
		interval:       time.Duration(intervalS) * time.Second,
		timeout:        time.Duration(timeoutS) * time.Second,
		maxFailedTimes: maxFailedTimes,
		addr:           addr,
		url:            url,
		statusOK:       false,
		statusNormalFn: statusNormalFn,
		statusFailedFn: statusFailedFn,
		ctx:            ctx,
		cancel:         cancel,
	}
}

func (monitor *HealthCheckMonitor) SetLogger(l log.Logger) {
	monitor.l = l
}

func (monitor *HealthCheckMonitor) Start() {
	go monitor.checkWorker()
}

func (monitor *HealthCheckMonitor) Stop() {
	monitor.cancel()
}

func (monitor *HealthCheckMonitor) checkWorker() {
	for {
		ctx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
		err := monitor.doCheck(ctx)

		// check if this monitor has been closed
		select {
		case <-ctx.Done():
			cancel()
			return
		default:
			cancel()
		}

		if err == nil {
			if monitor.l != nil {
				monitor.l.Trace("do one health check success")
			}
			if !monitor.statusOK && monitor.statusNormalFn != nil {
				if monitor.l != nil {
					monitor.l.Info("health check status change to success")
				}
				monitor.statusOK = true
				monitor.statusNormalFn()
			}
		} else {
			if monitor.l != nil {
				monitor.l.Warn("do one health check failed: %v", err)
			}
			monitor.failedTimes++
			if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
				if monitor.l != nil {
					monitor.l.Warn("health check status change to failed")
				}
				monitor.statusOK = false
				monitor.statusFailedFn()
			}
		}

		time.Sleep(monitor.interval)
	}
}

func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) error {
	switch monitor.checkType {
	case "tcp":
		return monitor.doTcpCheck(ctx)
	case "http":
		return monitor.doHttpCheck(ctx)
	default:
		return ErrHealthCheckType
	}
}

func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) error {
	// if tcp address is not specified, always return nil
	if monitor.addr == "" {
		return nil
	}

	var d net.Dialer
	conn, err := d.DialContext(ctx, "tcp", monitor.addr)
	if err != nil {
		return err
	}
	conn.Close()
	return nil
}

func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) error {
	req, err := http.NewRequest("GET", monitor.url, nil)
	if err != nil {
		return err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	if resp.StatusCode/100 != 2 {
		return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode)
	}
	return nil
}
<commit_msg>fix health check unclosed resp body, fix #1155<commit_after>// Copyright 2018 fatedier, fatedier@gmail.com
//
// 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 health

import (
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"github.com/fatedier/frp/utils/log"
)

var (
	ErrHealthCheckType = errors.New("error health check type")
)

type HealthCheckMonitor struct {
	checkType      string
	interval       time.Duration
	timeout        time.Duration
	maxFailedTimes int

	// For tcp
	addr string

	// For http
	url string

	failedTimes    uint64
	statusOK       bool
	statusNormalFn func()
	statusFailedFn func()

	ctx    context.Context
	cancel context.CancelFunc

	l log.Logger
}

func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string,
	statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor {

	if intervalS <= 0 {
		intervalS = 10
	}
	if timeoutS <= 0 {
		timeoutS = 3
	}
	if maxFailedTimes <= 0 {
		maxFailedTimes = 1
	}
	ctx, cancel := context.WithCancel(context.Background())
	return &HealthCheckMonitor{
		checkType:      checkType,
		interval:       time.Duration(intervalS) * time.Second,
		timeout:        time.Duration(timeoutS) * time.Second,
		maxFailedTimes: maxFailedTimes,
		addr:           addr,
		url:            url,
		statusOK:       false,
		statusNormalFn: statusNormalFn,
		statusFailedFn: statusFailedFn,
		ctx:            ctx,
		cancel:         cancel,
	}
}

func (monitor *HealthCheckMonitor) SetLogger(l log.Logger) {
	monitor.l = l
}

func (monitor *HealthCheckMonitor) Start() {
	go monitor.checkWorker()
}

func (monitor *HealthCheckMonitor) Stop() {
	monitor.cancel()
}

func (monitor *HealthCheckMonitor) checkWorker() {
	for {
		ctx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
		err := monitor.doCheck(ctx)

		// check if this monitor has been closed
		select {
		case <-ctx.Done():
			cancel()
			return
		default:
			cancel()
		}

		if err == nil {
			if monitor.l != nil {
				monitor.l.Trace("do one health check success")
			}
			if !monitor.statusOK && monitor.statusNormalFn != nil {
				if monitor.l != nil {
					monitor.l.Info("health check status change to success")
				}
				monitor.statusOK = true
				monitor.statusNormalFn()
			}
		} else {
			if monitor.l != nil {
				monitor.l.Warn("do one health check failed: %v", err)
			}
			monitor.failedTimes++
			if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
				if monitor.l != nil {
					monitor.l.Warn("health check status change to failed")
				}
				monitor.statusOK = false
				monitor.statusFailedFn()
			}
		}

		time.Sleep(monitor.interval)
	}
}

func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) error {
	switch monitor.checkType {
	case "tcp":
		return monitor.doTcpCheck(ctx)
	case "http":
		return monitor.doHttpCheck(ctx)
	default:
		return ErrHealthCheckType
	}
}

func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) error {
	// if tcp address is not specified, always return nil
	if monitor.addr == "" {
		return nil
	}

	var d net.Dialer
	conn, err := d.DialContext(ctx, "tcp", monitor.addr)
	if err != nil {
		return err
	}
	conn.Close()
	return nil
}

func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) error {
	req, err := http.NewRequest("GET", monitor.url, nil)
	if err != nil {
		return err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	io.Copy(ioutil.Discard, resp.Body)

	if resp.StatusCode/100 != 2 {
		return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode)
	}
	return nil
}
<|endoftext|>


<commit_before>package native

import (
	"net/http"
	"sync"

	"github.com/CyCoreSystems/ari"

	"golang.org/x/net/context"
	"golang.org/x/net/websocket"
)

// Options describes the options for connecting to
// a native Asterisk ARI server.
type Options struct {
	// Application is the the name of this ARI application
	Application string

	// URL is the root URL of the ARI server (asterisk box).
	// Default to http://localhost:8088/ari
	URL string

	// WebsocketURL is the URL for ARI Websocket events.
	// Defaults to the events directory of URL, with a protocol of ws.
	// Usually ws://localhost:8088/ari/events.
	WebsocketURL string

	// Username for ARI authentication
	Username string

	// Password for ARI authentication
	Password string
}

// Conn is a connection to a native ARI server
type Conn struct {
	Options *Options // client options

	WSConfig *websocket.Config // websocket connection configuration

	ReadyChan chan struct{}

	Bus *ari.Bus // event bus
	//events chan *Event // chan on which events are sent

	httpClient http.Client

	cancel context.CancelFunc
	mu     sync.Mutex
}

// New creates a new ari.Client connected to a native ARI server
func New(opts *Options) (*ari.Client, error) {

	conn := Conn{
		Options: opts,
	}

	//TODO: populate client
	return &ari.Client{
		Channel:     &nativeChannel{&conn},
		Asterisk:    &nativeAsterisk{&conn},
		Application: &nativeApplication{&conn},
		Playback:    &nativePlayback{&conn},
		Bridge:      &nativeBridge{&conn},
		Mailbox:     &nativeMailbox{&conn},
		Endpoint:    &nativeEndpoint{&conn},
	}, nil
}
<commit_msg>v3 - native client - split out newConn and New<commit_after>package native

import (
	"net/http"
	"sync"

	"github.com/CyCoreSystems/ari"

	"golang.org/x/net/context"
	"golang.org/x/net/websocket"
)

// Options describes the options for connecting to
// a native Asterisk ARI server.
type Options struct {
	// Application is the the name of this ARI application
	Application string

	// URL is the root URL of the ARI server (asterisk box).
	// Default to http://localhost:8088/ari
	URL string

	// WebsocketURL is the URL for ARI Websocket events.
	// Defaults to the events directory of URL, with a protocol of ws.
	// Usually ws://localhost:8088/ari/events.
	WebsocketURL string

	// Username for ARI authentication
	Username string

	// Password for ARI authentication
	Password string
}

// Conn is a connection to a native ARI server
type Conn struct {
	Options *Options // client options

	WSConfig *websocket.Config // websocket connection configuration

	ReadyChan chan struct{}

	Bus *ari.Bus // event bus
	//events chan *Event // chan on which events are sent

	httpClient http.Client

	cancel context.CancelFunc
	mu     sync.Mutex
}

func newConn(opts *Options) *Conn {
	return &Conn{
		Options: opts,
	}
}

// New creates a new ari.Client connected to a native ARI server
func New(opts *Options) (*ari.Client, error) {

	conn := newConn(opts)

	return &ari.Client{
		Channel:     &nativeChannel{&conn},
		Asterisk:    &nativeAsterisk{&conn},
		Application: &nativeApplication{&conn},
		Playback:    &nativePlayback{&conn},
		Bridge:      &nativeBridge{&conn},
		Mailbox:     &nativeMailbox{&conn},
		Endpoint:    &nativeEndpoint{&conn},
	}, nil
}
<|endoftext|>


<commit_before>package pipelines_test

import (
	"github.com/concourse/testflight/gitserver"
	"github.com/concourse/testflight/guidserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("A job with a step that retries", func() {
	var guidServer *guidserver.Server
	var originGitServer *gitserver.Server

	BeforeEach(func() {
		guidServer = guidserver.Start(guidServerRootfs, gardenClient)
		originGitServer = gitserver.Start(gitServerRootfs, gardenClient)

		configurePipeline(
			"-c", "fixtures/retry.yml",
			"-v", "testflight-helper-image="+guidServerRootfs,
			"-v", "guid-server-register-command="+guidServer.RegisterCommand(),
			"-v", "guid-server-registrations-command="+guidServer.RegistrationsCommand(),
			"-v", "origin-git-server="+originGitServer.URI(),
		)

		originGitServer.Commit()
	})

	AfterEach(func() {
		originGitServer.Stop()
		guidServer.Stop()
	})

	It("retries until the step succeeds", func() {
		watch := flyWatch("retry-job")
		Expect(watch).To(gexec.Exit(0))

		Expect(watch).To(gbytes.Say("initializing"))
		Expect(watch).To(gbytes.Say("registrations: 1; failing"))
		Expect(watch).To(gbytes.Say("registrations: 2; failing"))
		Expect(watch).To(gbytes.Say("registrations: 3; success!"))
		Expect(watch).ToNot(gbytes.Say("registrations:"))
		Expect(watch).To(gbytes.Say("succeeded"))
	})
})
<commit_msg>test display attempts in hijack list<commit_after>package pipelines_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/concourse/testflight/gitserver"
	"github.com/concourse/testflight/guidserver"
	"github.com/concourse/testflight/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("A job with a step that retries", func() {
	var guidServer *guidserver.Server
	var originGitServer *gitserver.Server

	BeforeEach(func() {
		guidServer = guidserver.Start(guidServerRootfs, gardenClient)
		originGitServer = gitserver.Start(gitServerRootfs, gardenClient)

		configurePipeline(
			"-c", "fixtures/retry.yml",
			"-v", "testflight-helper-image="+guidServerRootfs,
			"-v", "guid-server-register-command="+guidServer.RegisterCommand(),
			"-v", "guid-server-registrations-command="+guidServer.RegistrationsCommand(),
			"-v", "origin-git-server="+originGitServer.URI(),
		)

		originGitServer.Commit()
	})

	AfterEach(func() {
		originGitServer.Stop()
		guidServer.Stop()
	})

	It("retries until the step succeeds", func() {
		watch := flyWatch("retry-job")
		Expect(watch).To(gexec.Exit(0))

		Expect(watch).To(gbytes.Say("initializing"))
		Expect(watch).To(gbytes.Say("registrations: 1; failing"))
		Expect(watch).To(gbytes.Say("registrations: 2; failing"))
		Expect(watch).To(gbytes.Say("registrations: 3; success!"))
		Expect(watch).ToNot(gbytes.Say("registrations:"))
		Expect(watch).To(gbytes.Say("succeeded"))
	})

	Context("hijacking jobs with retry steps", func() {
		var hijackS *gexec.Session
		var hijack *exec.Cmd

		XIt("permits hijacking a specific attempt", func() {
			tmpdir, err := ioutil.TempDir("", "fly-test")
			Expect(err).NotTo(HaveOccurred())

			fixture := filepath.Join(tmpdir, "fixture")

			err = os.MkdirAll(fixture, 0755)
			Expect(err).NotTo(HaveOccurred())

			err = ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
mkfifo /tmp/fifo
cat < /tmp/fifo
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())
			hijack = exec.Command(flyBin, "-t", targetedConcourse, "hijack", "-j", pipelineName+"/retry-job", "-s", "curl-server-until-3-curls", "--attempts", "2", "--", "sh", "-c", "echo $RETRY_NUMBER > /tmp/fifo")
			hijack.Dir = fixture
			hijackS = helpers.StartFly(hijack)
			Eventually(hijackS).Should(gbytes.Say("2"))
			Eventually(hijackS).Should(gexec.Exit())
		})

		It("correctly displays information about attempts", func() {
			watch := flyWatch("retry-job")
			Expect(watch).To(gexec.Exit(0))

			hijack = exec.Command(flyBin, "-t", targetedConcourse, "hijack", "-j", pipelineName+"/retry-job", "-s", "curl-server-until-3-curls", "--", "sh", "-c", "exit")
			hijackS = helpers.StartFly(hijack)
			Eventually(hijackS).Should(gbytes.Say("1: build #1, step: curl-server-until-3-curls, type: task, attempts: \\[[1-3]\\]"))
			Eventually(hijackS).Should(gbytes.Say("2: build #1, step: curl-server-until-3-curls, type: task, attempts: \\[[1-3]\\]"))
			Eventually(hijackS).Should(gbytes.Say("3: build #1, step: curl-server-until-3-curls, type: task, attempts: \\[[1-3]\\]"))
			hijackS.Out.Write([]byte("2"))
			Eventually(hijackS).Should(gexec.Exit())
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2018-2019 Authors of Cilium
//
// 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 endpoint

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"time"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/common/addressing"
	"github.com/cilium/cilium/pkg/defaults"
	"github.com/cilium/cilium/pkg/endpoint/regeneration"
	"github.com/cilium/cilium/pkg/fqdn"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/identity/cache"
	"github.com/cilium/cilium/pkg/ipcache"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/mac"
	"github.com/cilium/cilium/pkg/option"

	"github.com/sirupsen/logrus"
)

// ReadEPsFromDirNames returns a mapping of endpoint ID to endpoint of endpoints
// from a list of directory names that can possible contain an endpoint.
func ReadEPsFromDirNames(owner regeneration.Owner, basePath string, eptsDirNames []string) map[uint16]*Endpoint {
	possibleEPs := map[uint16]*Endpoint{}
	for _, epDirName := range eptsDirNames {
		epDir := filepath.Join(basePath, epDirName)
		readDir := func() string {
			scopedLog := log.WithFields(logrus.Fields{
				logfields.EndpointID: epDirName,
				logfields.Path:       filepath.Join(epDir, common.CHeaderFileName),
			})
			scopedLog.Debug("Reading directory")
			epFiles, err := ioutil.ReadDir(epDir)
			if err != nil {
				scopedLog.WithError(err).Warn("Error while reading directory. Ignoring it...")
				return ""
			}
			cHeaderFile := common.FindEPConfigCHeader(epDir, epFiles)
			if cHeaderFile == "" {
				return ""
			}
			return cHeaderFile
		}
		// There's an odd issue where the first read dir doesn't work.
		cHeaderFile := readDir()
		if cHeaderFile == "" {
			cHeaderFile = readDir()
		}

		scopedLog := log.WithFields(logrus.Fields{
			logfields.EndpointID: epDirName,
			logfields.Path:       cHeaderFile,
		})

		if cHeaderFile == "" {
			scopedLog.Warning("C header file not found. Ignoring endpoint")
			continue
		}

		scopedLog.Debug("Found endpoint C header file")

		strEp, err := common.GetCiliumVersionString(cHeaderFile)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to read the C header file")
			continue
		}
		ep, err := parseEndpoint(owner, strEp)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to parse the C header file")
			continue
		}
		if _, ok := possibleEPs[ep.ID]; ok {
			// If the endpoint already exists then give priority to the directory
			// that contains an endpoint that didn't fail to be build.
			if strings.HasSuffix(ep.DirectoryPath(), epDirName) {
				possibleEPs[ep.ID] = ep
			}
		} else {
			possibleEPs[ep.ID] = ep
		}
	}
	return possibleEPs
}

// RegenerateAfterRestore performs the following operations on the specified
// Endpoint:
// * allocates an identity for the Endpoint
// * regenerates the endpoint
// Returns an error if any operation fails while trying to perform the above
// operations.
func (e *Endpoint) RegenerateAfterRestore() error {
	if err := e.restoreIdentity(); err != nil {
		return err
	}

	scopedLog := log.WithField(logfields.EndpointID, e.ID)

	regenerationMetadata := &regeneration.ExternalRegenerationMetadata{
		Reason: "syncing state to host",
	}
	if buildSuccess := <-e.Regenerate(regenerationMetadata); !buildSuccess {
		scopedLog.Warn("Failed while regenerating endpoint")
		return fmt.Errorf("failed while regenerating endpoint")
	}

	// NOTE: unconditionalRLock is used here because it's used only for logging an already restored endpoint
	e.unconditionalRLock()
	scopedLog.WithField(logfields.IPAddr, []string{e.IPv4.String(), e.IPv6.String()}).Info("Restored endpoint")
	e.runlock()
	return nil
}

func (e *Endpoint) restoreIdentity() error {
	if err := e.rlockAlive(); err != nil {
		e.logDisconnectedMutexAction(err, "before filtering labels during regenerating restored endpoint")
		return err
	}
	scopedLog := log.WithField(logfields.EndpointID, e.ID)
	// Filter the restored labels with the new daemon's filter
	l, _ := labels.FilterLabels(e.OpLabels.AllLabels())
	e.runlock()

	allocateCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout)
	defer cancel()
	identity, _, err := cache.AllocateIdentity(allocateCtx, e.owner, l)

	if err != nil {
		scopedLog.WithError(err).Warn("Unable to restore endpoint")
		return err
	}

	// Wait for initial identities and ipcache from the
	// kvstore before doing any policy calculation for
	// endpoints that don't have a fixed identity or are
	// not well known.
	if !identity.IsFixed() && !identity.IsWellKnown() {
		identityCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout)
		defer cancel()

		err = cache.WaitForInitialGlobalIdentities(identityCtx)
		if err != nil {
			scopedLog.WithError(err).Warn("Failed while waiting for initial global identities")
			return err
		}
		if option.Config.KVStore != "" {
			ipcache.WaitForKVStoreSync()
		}
	}

	if err := e.lockAlive(); err != nil {
		scopedLog.Warn("Endpoint to restore has been deleted")
		return err
	}

	e.setState(StateRestoring, "Synchronizing endpoint labels with KVStore")

	if e.SecurityIdentity != nil {
		if oldSecID := e.SecurityIdentity.ID; identity.ID != oldSecID {
			log.WithFields(logrus.Fields{
				logfields.EndpointID:              e.ID,
				logfields.IdentityLabels + ".old": oldSecID,
				logfields.IdentityLabels + ".new": identity.ID,
			}).Info("Security identity for endpoint is different from the security identity restored for the endpoint")

			// The identity of the endpoint being
			// restored has changed. This can be
			// caused by two main reasons:
			//
			// 1) Cilium has been upgraded,
			// downgraded or the configuration has
			// changed and the new version or
			// configuration causes different
			// labels to be considered security
			// relevant for this endpoint.
			//
			// Immediately using the identity may
			// cause connectivity problems if this
			// is the first endpoint in the cluster
			// to use the new identity. All other
			// nodes will not have had a chance to
			// adjust the security policies for
			// their endpoints. Hence, apply a
			// grace period to allow for the
			// update. It is not required to check
			// any local endpoints for potential
			// outdated security rules, the
			// notification of the new security
			// identity will have been received and
			// will trigger the necessary
			// recalculation of all local
			// endpoints.
			//
			// 2) The identity is outdated as the
			// state in the kvstore has changed.
			// This reason would justify an
			// immediate use of the new identity
			// but given the current identity is
			// already in place, it is also correct
			// to continue using it for the
			// duration of a grace period.
			time.Sleep(defaults.IdentityChangeGracePeriod)
		}
	}
	// The identity of a freshly restored endpoint is incomplete due to some
	// parts of the identity not being marshaled to JSON. Hence we must set
	// the identity even if has not changed.
	e.SetIdentity(identity, true)
	e.unlock()

	return nil
}

// toSerializedEndpoint converts the Endpoint to its corresponding
// serializableEndpoint, which contains all of the fields that are needed upon
// restoring an Endpoint after cilium-agent restarts.
func (e *Endpoint) toSerializedEndpoint() *serializableEndpoint {

	return &serializableEndpoint{
		ID:                    e.ID,
		ContainerName:         e.containerName,
		ContainerID:           e.containerID,
		DockerNetworkID:       e.dockerNetworkID,
		DockerEndpointID:      e.dockerEndpointID,
		DatapathMapID:         e.datapathMapID,
		IfName:                e.ifName,
		IfIndex:               e.ifIndex,
		OpLabels:              e.OpLabels,
		LXCMAC:                e.mac,
		IPv6:                  e.IPv6,
		IPv4:                  e.IPv4,
		NodeMAC:               e.nodeMAC,
		SecurityIdentity:      e.SecurityIdentity,
		Options:               e.Options,
		DNSHistory:            e.DNSHistory,
		K8sPodName:            e.K8sPodName,
		K8sNamespace:          e.K8sNamespace,
		DatapathConfiguration: e.DatapathConfiguration,
	}
}

// serializableEndpoint contains the fields from an Endpoint which are needed to be
// restored if cilium-agent restarts.
//
//
// WARNING - STABLE API
// This structure is written as JSON to StateDir/{ID}/lxc_config.h to allow to
// restore endpoints when the agent is being restarted. The restore operation
// will read the file and re-create all endpoints with all fields which are not
// marked as private to JSON marshal. Do NOT modify this structure in ways which
// is not JSON forward compatible.
//
type serializableEndpoint struct {
	// ID of the endpoint, unique in the scope of the node
	ID uint16

	// containerName is the name given to the endpoint by the container runtime
	ContainerName string

	// containerID is the container ID that docker has assigned to the endpoint
	// Note: The JSON tag was kept for backward compatibility.
	ContainerID string `json:"dockerID,omitempty"`

	// dockerNetworkID is the network ID of the libnetwork network if the
	// endpoint is a docker managed container which uses libnetwork
	DockerNetworkID string

	// dockerEndpointID is the Docker network endpoint ID if managed by
	// libnetwork
	DockerEndpointID string

	// Corresponding BPF map identifier for tail call map of ipvlan datapath
	DatapathMapID int

	// ifName is the name of the host facing interface (veth pair) which
	// connects into the endpoint
	IfName string

	// ifIndex is the interface index of the host face interface (veth pair)
	IfIndex int

	// OpLabels is the endpoint's label configuration
	//
	// FIXME: Rename this field to Labels
	OpLabels labels.OpLabels

	// mac is the MAC address of the endpoint
	//
	// FIXME: Rename this field to MAC
	LXCMAC mac.MAC // Container MAC address.

	// IPv6 is the IPv6 address of the endpoint
	IPv6 addressing.CiliumIPv6

	// IPv4 is the IPv4 address of the endpoint
	IPv4 addressing.CiliumIPv4

	// nodeMAC is the MAC of the node (agent). The MAC is different for every endpoint.
	NodeMAC mac.MAC

	// SecurityIdentity is the security identity of this endpoint. This is computed from
	// the endpoint's labels.
	SecurityIdentity *identity.Identity `json:"SecLabel"`

	// Options determine the datapath configuration of the endpoint.
	Options *option.IntOptions
	// DNSHistory is the collection of still-valid DNS responses intercepted for
	// this endpoint.
	DNSHistory *fqdn.DNSCache

	// K8sPodName is the Kubernetes pod name of the endpoint
	K8sPodName string

	// K8sNamespace is the Kubernetes namespace of the endpoint
	K8sNamespace string

	// DatapathConfiguration is the endpoint's datapath configuration as
	// passed in via the plugin that created the endpoint, e.g. the CNI
	// plugin which performed the plumbing will enable certain datapath
	// features according to the mode selected.
	DatapathConfiguration models.EndpointDatapathConfiguration
}

// UnmarshalJSON expects that the contents of `raw` are a serializableEndpoint,
// which is then converted into an Endpoint.
func (ep *Endpoint) UnmarshalJSON(raw []byte) error {
	// We may have to populate structures in the Endpoint manually to do the
	// translation from serializableEndpoint --> Endpoint.
	log.Info("parsing serializableEndpoint marshaled into headerfile")
	restoredEp := &serializableEndpoint{
		OpLabels:   labels.NewOpLabels(),
		DNSHistory: fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost),
	}
	if err := json.Unmarshal(raw, restoredEp); err != nil {
		return fmt.Errorf("error unmarshaling serializableEndpoint from base64 representation: %s", err)
	}

	ep.fromSerializedEndpoint(restoredEp)
	return nil
}

// MarshalJSON marshals the Endpoint as its serializableEndpoint representation.
func (ep *Endpoint) MarshalJSON() ([]byte, error) {
	return json.Marshal(ep.toSerializedEndpoint())
}

func (ep *Endpoint) fromSerializedEndpoint(r *serializableEndpoint) {
	ep.ID = r.ID
	ep.containerName = r.ContainerName
	ep.containerID = r.ContainerID
	ep.dockerNetworkID = r.DockerNetworkID
	ep.dockerEndpointID = r.DockerEndpointID
	ep.datapathMapID = r.DatapathMapID
	ep.ifName = r.IfName
	ep.ifIndex = r.IfIndex
	ep.OpLabels = r.OpLabels
	ep.mac = r.LXCMAC
	ep.IPv6 = r.IPv6
	ep.IPv4 = r.IPv4
	ep.nodeMAC = r.NodeMAC
	ep.SecurityIdentity = r.SecurityIdentity
	ep.DNSHistory = r.DNSHistory
	ep.K8sPodName = r.K8sPodName
	ep.K8sNamespace = r.K8sNamespace
	ep.DatapathConfiguration = r.DatapathConfiguration
	ep.Options = r.Options
}
<commit_msg>endpoint: Remove useless restore log message<commit_after>// Copyright 2018-2019 Authors of Cilium
//
// 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 endpoint

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"time"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/common/addressing"
	"github.com/cilium/cilium/pkg/defaults"
	"github.com/cilium/cilium/pkg/endpoint/regeneration"
	"github.com/cilium/cilium/pkg/fqdn"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/identity/cache"
	"github.com/cilium/cilium/pkg/ipcache"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/mac"
	"github.com/cilium/cilium/pkg/option"

	"github.com/sirupsen/logrus"
)

// ReadEPsFromDirNames returns a mapping of endpoint ID to endpoint of endpoints
// from a list of directory names that can possible contain an endpoint.
func ReadEPsFromDirNames(owner regeneration.Owner, basePath string, eptsDirNames []string) map[uint16]*Endpoint {
	possibleEPs := map[uint16]*Endpoint{}
	for _, epDirName := range eptsDirNames {
		epDir := filepath.Join(basePath, epDirName)
		readDir := func() string {
			scopedLog := log.WithFields(logrus.Fields{
				logfields.EndpointID: epDirName,
				logfields.Path:       filepath.Join(epDir, common.CHeaderFileName),
			})
			scopedLog.Debug("Reading directory")
			epFiles, err := ioutil.ReadDir(epDir)
			if err != nil {
				scopedLog.WithError(err).Warn("Error while reading directory. Ignoring it...")
				return ""
			}
			cHeaderFile := common.FindEPConfigCHeader(epDir, epFiles)
			if cHeaderFile == "" {
				return ""
			}
			return cHeaderFile
		}
		// There's an odd issue where the first read dir doesn't work.
		cHeaderFile := readDir()
		if cHeaderFile == "" {
			cHeaderFile = readDir()
		}

		scopedLog := log.WithFields(logrus.Fields{
			logfields.EndpointID: epDirName,
			logfields.Path:       cHeaderFile,
		})

		if cHeaderFile == "" {
			scopedLog.Warning("C header file not found. Ignoring endpoint")
			continue
		}

		scopedLog.Debug("Found endpoint C header file")

		strEp, err := common.GetCiliumVersionString(cHeaderFile)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to read the C header file")
			continue
		}
		ep, err := parseEndpoint(owner, strEp)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to parse the C header file")
			continue
		}
		if _, ok := possibleEPs[ep.ID]; ok {
			// If the endpoint already exists then give priority to the directory
			// that contains an endpoint that didn't fail to be build.
			if strings.HasSuffix(ep.DirectoryPath(), epDirName) {
				possibleEPs[ep.ID] = ep
			}
		} else {
			possibleEPs[ep.ID] = ep
		}
	}
	return possibleEPs
}

// RegenerateAfterRestore performs the following operations on the specified
// Endpoint:
// * allocates an identity for the Endpoint
// * regenerates the endpoint
// Returns an error if any operation fails while trying to perform the above
// operations.
func (e *Endpoint) RegenerateAfterRestore() error {
	if err := e.restoreIdentity(); err != nil {
		return err
	}

	scopedLog := log.WithField(logfields.EndpointID, e.ID)

	regenerationMetadata := &regeneration.ExternalRegenerationMetadata{
		Reason: "syncing state to host",
	}
	if buildSuccess := <-e.Regenerate(regenerationMetadata); !buildSuccess {
		scopedLog.Warn("Failed while regenerating endpoint")
		return fmt.Errorf("failed while regenerating endpoint")
	}

	// NOTE: unconditionalRLock is used here because it's used only for logging an already restored endpoint
	e.unconditionalRLock()
	scopedLog.WithField(logfields.IPAddr, []string{e.IPv4.String(), e.IPv6.String()}).Info("Restored endpoint")
	e.runlock()
	return nil
}

func (e *Endpoint) restoreIdentity() error {
	if err := e.rlockAlive(); err != nil {
		e.logDisconnectedMutexAction(err, "before filtering labels during regenerating restored endpoint")
		return err
	}
	scopedLog := log.WithField(logfields.EndpointID, e.ID)
	// Filter the restored labels with the new daemon's filter
	l, _ := labels.FilterLabels(e.OpLabels.AllLabels())
	e.runlock()

	allocateCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout)
	defer cancel()
	identity, _, err := cache.AllocateIdentity(allocateCtx, e.owner, l)

	if err != nil {
		scopedLog.WithError(err).Warn("Unable to restore endpoint")
		return err
	}

	// Wait for initial identities and ipcache from the
	// kvstore before doing any policy calculation for
	// endpoints that don't have a fixed identity or are
	// not well known.
	if !identity.IsFixed() && !identity.IsWellKnown() {
		identityCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout)
		defer cancel()

		err = cache.WaitForInitialGlobalIdentities(identityCtx)
		if err != nil {
			scopedLog.WithError(err).Warn("Failed while waiting for initial global identities")
			return err
		}
		if option.Config.KVStore != "" {
			ipcache.WaitForKVStoreSync()
		}
	}

	if err := e.lockAlive(); err != nil {
		scopedLog.Warn("Endpoint to restore has been deleted")
		return err
	}

	e.setState(StateRestoring, "Synchronizing endpoint labels with KVStore")

	if e.SecurityIdentity != nil {
		if oldSecID := e.SecurityIdentity.ID; identity.ID != oldSecID {
			log.WithFields(logrus.Fields{
				logfields.EndpointID:              e.ID,
				logfields.IdentityLabels + ".old": oldSecID,
				logfields.IdentityLabels + ".new": identity.ID,
			}).Info("Security identity for endpoint is different from the security identity restored for the endpoint")

			// The identity of the endpoint being
			// restored has changed. This can be
			// caused by two main reasons:
			//
			// 1) Cilium has been upgraded,
			// downgraded or the configuration has
			// changed and the new version or
			// configuration causes different
			// labels to be considered security
			// relevant for this endpoint.
			//
			// Immediately using the identity may
			// cause connectivity problems if this
			// is the first endpoint in the cluster
			// to use the new identity. All other
			// nodes will not have had a chance to
			// adjust the security policies for
			// their endpoints. Hence, apply a
			// grace period to allow for the
			// update. It is not required to check
			// any local endpoints for potential
			// outdated security rules, the
			// notification of the new security
			// identity will have been received and
			// will trigger the necessary
			// recalculation of all local
			// endpoints.
			//
			// 2) The identity is outdated as the
			// state in the kvstore has changed.
			// This reason would justify an
			// immediate use of the new identity
			// but given the current identity is
			// already in place, it is also correct
			// to continue using it for the
			// duration of a grace period.
			time.Sleep(defaults.IdentityChangeGracePeriod)
		}
	}
	// The identity of a freshly restored endpoint is incomplete due to some
	// parts of the identity not being marshaled to JSON. Hence we must set
	// the identity even if has not changed.
	e.SetIdentity(identity, true)
	e.unlock()

	return nil
}

// toSerializedEndpoint converts the Endpoint to its corresponding
// serializableEndpoint, which contains all of the fields that are needed upon
// restoring an Endpoint after cilium-agent restarts.
func (e *Endpoint) toSerializedEndpoint() *serializableEndpoint {

	return &serializableEndpoint{
		ID:                    e.ID,
		ContainerName:         e.containerName,
		ContainerID:           e.containerID,
		DockerNetworkID:       e.dockerNetworkID,
		DockerEndpointID:      e.dockerEndpointID,
		DatapathMapID:         e.datapathMapID,
		IfName:                e.ifName,
		IfIndex:               e.ifIndex,
		OpLabels:              e.OpLabels,
		LXCMAC:                e.mac,
		IPv6:                  e.IPv6,
		IPv4:                  e.IPv4,
		NodeMAC:               e.nodeMAC,
		SecurityIdentity:      e.SecurityIdentity,
		Options:               e.Options,
		DNSHistory:            e.DNSHistory,
		K8sPodName:            e.K8sPodName,
		K8sNamespace:          e.K8sNamespace,
		DatapathConfiguration: e.DatapathConfiguration,
	}
}

// serializableEndpoint contains the fields from an Endpoint which are needed to be
// restored if cilium-agent restarts.
//
//
// WARNING - STABLE API
// This structure is written as JSON to StateDir/{ID}/lxc_config.h to allow to
// restore endpoints when the agent is being restarted. The restore operation
// will read the file and re-create all endpoints with all fields which are not
// marked as private to JSON marshal. Do NOT modify this structure in ways which
// is not JSON forward compatible.
//
type serializableEndpoint struct {
	// ID of the endpoint, unique in the scope of the node
	ID uint16

	// containerName is the name given to the endpoint by the container runtime
	ContainerName string

	// containerID is the container ID that docker has assigned to the endpoint
	// Note: The JSON tag was kept for backward compatibility.
	ContainerID string `json:"dockerID,omitempty"`

	// dockerNetworkID is the network ID of the libnetwork network if the
	// endpoint is a docker managed container which uses libnetwork
	DockerNetworkID string

	// dockerEndpointID is the Docker network endpoint ID if managed by
	// libnetwork
	DockerEndpointID string

	// Corresponding BPF map identifier for tail call map of ipvlan datapath
	DatapathMapID int

	// ifName is the name of the host facing interface (veth pair) which
	// connects into the endpoint
	IfName string

	// ifIndex is the interface index of the host face interface (veth pair)
	IfIndex int

	// OpLabels is the endpoint's label configuration
	//
	// FIXME: Rename this field to Labels
	OpLabels labels.OpLabels

	// mac is the MAC address of the endpoint
	//
	// FIXME: Rename this field to MAC
	LXCMAC mac.MAC // Container MAC address.

	// IPv6 is the IPv6 address of the endpoint
	IPv6 addressing.CiliumIPv6

	// IPv4 is the IPv4 address of the endpoint
	IPv4 addressing.CiliumIPv4

	// nodeMAC is the MAC of the node (agent). The MAC is different for every endpoint.
	NodeMAC mac.MAC

	// SecurityIdentity is the security identity of this endpoint. This is computed from
	// the endpoint's labels.
	SecurityIdentity *identity.Identity `json:"SecLabel"`

	// Options determine the datapath configuration of the endpoint.
	Options *option.IntOptions
	// DNSHistory is the collection of still-valid DNS responses intercepted for
	// this endpoint.
	DNSHistory *fqdn.DNSCache

	// K8sPodName is the Kubernetes pod name of the endpoint
	K8sPodName string

	// K8sNamespace is the Kubernetes namespace of the endpoint
	K8sNamespace string

	// DatapathConfiguration is the endpoint's datapath configuration as
	// passed in via the plugin that created the endpoint, e.g. the CNI
	// plugin which performed the plumbing will enable certain datapath
	// features according to the mode selected.
	DatapathConfiguration models.EndpointDatapathConfiguration
}

// UnmarshalJSON expects that the contents of `raw` are a serializableEndpoint,
// which is then converted into an Endpoint.
func (ep *Endpoint) UnmarshalJSON(raw []byte) error {
	// We may have to populate structures in the Endpoint manually to do the
	// translation from serializableEndpoint --> Endpoint.
	restoredEp := &serializableEndpoint{
		OpLabels:   labels.NewOpLabels(),
		DNSHistory: fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost),
	}
	if err := json.Unmarshal(raw, restoredEp); err != nil {
		return fmt.Errorf("error unmarshaling serializableEndpoint from base64 representation: %s", err)
	}

	ep.fromSerializedEndpoint(restoredEp)
	return nil
}

// MarshalJSON marshals the Endpoint as its serializableEndpoint representation.
func (ep *Endpoint) MarshalJSON() ([]byte, error) {
	return json.Marshal(ep.toSerializedEndpoint())
}

func (ep *Endpoint) fromSerializedEndpoint(r *serializableEndpoint) {
	ep.ID = r.ID
	ep.containerName = r.ContainerName
	ep.containerID = r.ContainerID
	ep.dockerNetworkID = r.DockerNetworkID
	ep.dockerEndpointID = r.DockerEndpointID
	ep.datapathMapID = r.DatapathMapID
	ep.ifName = r.IfName
	ep.ifIndex = r.IfIndex
	ep.OpLabels = r.OpLabels
	ep.mac = r.LXCMAC
	ep.IPv6 = r.IPv6
	ep.IPv4 = r.IPv4
	ep.nodeMAC = r.NodeMAC
	ep.SecurityIdentity = r.SecurityIdentity
	ep.DNSHistory = r.DNSHistory
	ep.K8sPodName = r.K8sPodName
	ep.K8sNamespace = r.K8sNamespace
	ep.DatapathConfiguration = r.DatapathConfiguration
	ep.Options = r.Options
}
<|endoftext|>


<commit_before>/*
	Package clientCore implements all the necessary functions for the mix client, i.e., the core of the client
	which allows to process the received cryptographic packets.
*/

package clientCore

import (
	"anonymous-messaging/config"
	"anonymous-messaging/helpers"
	"anonymous-messaging/logging"
	sphinx "anonymous-messaging/sphinx"

	"github.com/protobuf/proto"

	"crypto/elliptic"
	"errors"
)

var logLocal = logging.PackageLogger()

type NetworkPKI struct {
	Mixes     []config.MixConfig
	Providers []config.MixConfig
}

type MixClient interface {
	EncodeIntoSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error)
	DecodeSphinxPacket(packet sphinx.SphinxPacket) (sphinx.SphinxPacket, error)
	GetPublicKey() []byte
}

type CryptoClient struct {
	pubKey   []byte
	prvKey   []byte
	curve    elliptic.Curve
	Provider config.MixConfig
	Network  NetworkPKI
}

const (
	desiredRateParameter = 5
	pathLength           = 2
)

// CreateSphinxPacket responsible for sending a real message. Takes as input the message string
// and the public information about the destination.
// The function generates a random path and a set of random values from exponential distribution.
// Given those values it triggers the encode function, which packs the message into the
// sphinx cryptographic packet format. Next, the encoded packet is combined with a
// flag signaling that this is a usual network packet, and passed to be send.
// The function returns an error if any issues occurred.
func (c *CryptoClient) createSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error) {

	path, err := c.buildPath(recipient)
	if err != nil {
		logLocal.WithError(err).Error("Error in CreateSphinxPacket - generating random path failed")
		return nil, err
	}

	delays, err := c.generateDelaySequence(desiredRateParameter, path.Len())
	if err != nil {
		logLocal.WithError(err).Error("Error in CreateSphinxPacket - generating sequence of delays failed")
		return nil, err
	}

	sphinxPacket, err := sphinx.PackForwardMessage(c.curve, path, delays, message)
	if err != nil {
		logLocal.WithError(err).Error("Error in CreateSphinxPacket - the pack procedure failed")
		return nil, err
	}

	return proto.Marshal(&sphinxPacket)
}

// buildPath builds a path containing the sender's provider,
// a sequence (of length pre-defined in a config file) of randomly
// selected mixes and the recipient's provider
func (c *CryptoClient) buildPath(recipient config.ClientConfig) (config.E2EPath, error) {
	mixSeq, err := c.getRandomMixSequence(c.Network.Mixes, pathLength)
	if err != nil {
		logLocal.WithError(err).Error("Error in buildPath - generating random mix path failed")
		return config.E2EPath{}, err
	}
	path := config.E2EPath{IngressProvider: c.Provider, Mixes: mixSeq, EgressProvider: *recipient.Provider, Recipient: recipient}
	return path, nil
}

// getRandomMixSequence generates a random sequence of given length from all possible mixes.
// If the list of all active mixes is empty or the given length is larger than the set of active mixes,
// an error is returned.
func (c *CryptoClient) getRandomMixSequence(mixes []config.MixConfig, length int) ([]config.MixConfig, error) {
	if len(mixes) == 0 || mixes == nil {
		return nil, errors.New("cannot take a mix sequence from an empty list")
	}
	if length > len(mixes) {
		return mixes, nil
	} else {
		randomSeq, err := helpers.RandomSample(mixes, length)
		if err != nil {
			logLocal.WithError(err).Error("Error in getRandomMixSequence - sampling procedure failed")
			return nil, err
		}
		return randomSeq, nil
	}
}

// generateDelaySequence generates a given length sequence of float64 values. Values are generated
// following the exponential distribution. generateDelaySequence returnes a sequence or an error
// if any of the values could not be generate.
func (c *CryptoClient) generateDelaySequence(desiredRateParameter float64, length int) ([]float64, error) {
	var delays []float64
	for i := 0; i < length; i++ {
		d, err := helpers.RandomExponential(desiredRateParameter)
		if err != nil {
			logLocal.WithError(err).Error("Error in generateDelaySequence - generating random exponential sample failed")
			return nil, err
		}
		delays = append(delays, d)
	}
	return delays, nil
}

// EncodeMessage encodes given message into the Sphinx packet format. EncodeMessage takes as inputs
// the message, path which the packet should traverse, including the destination, and a set of delays.
// EncodeMessage returns the byte representation of the packet or an error if the packet could not be created.
func (c *CryptoClient) EncodeIntoSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error) {

	packet, err := c.createSphinxPacket(message, recipient)
	if err != nil {
		logLocal.WithError(err).Error("Error in EncodeMessage - the pack procedure failed")
		return nil, err
	}
	return packet, err
}

// DecodeMessage decodes the received sphinx packet.
// TODO: this function is finished yet.
func (c *CryptoClient) DecodeSphinxPacket(packet sphinx.SphinxPacket) (sphinx.SphinxPacket, error) {
	return packet, nil
}

func (c *CryptoClient) GetPublicKey() []byte {
	return c.pubKey
}

func NewCryptoClient(id string, pubKey, privKey []byte, curve elliptic.Curve, provider config.MixConfig, network NetworkPKI) *CryptoClient {
	return &CryptoClient{pubKey: pubKey, prvKey: privKey, curve: curve, Provider: provider, Network: network}
}
<commit_msg>Update doc string for EncodeIntoSphinxPacket function<commit_after>/*
	Package clientCore implements all the necessary functions for the mix client, i.e., the core of the client
	which allows to process the received cryptographic packets.
*/

package clientCore

import (
	"anonymous-messaging/config"
	"anonymous-messaging/helpers"
	"anonymous-messaging/logging"
	sphinx "anonymous-messaging/sphinx"

	"github.com/protobuf/proto"

	"crypto/elliptic"
	"errors"
)

var logLocal = logging.PackageLogger()

type NetworkPKI struct {
	Mixes     []config.MixConfig
	Providers []config.MixConfig
}

type MixClient interface {
	EncodeIntoSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error)
	DecodeSphinxPacket(packet sphinx.SphinxPacket) (sphinx.SphinxPacket, error)
	GetPublicKey() []byte
}

type CryptoClient struct {
	pubKey   []byte
	prvKey   []byte
	curve    elliptic.Curve
	Provider config.MixConfig
	Network  NetworkPKI
}

const (
	desiredRateParameter = 5
	pathLength           = 2
)

// CreateSphinxPacket responsible for sending a real message. Takes as input the message string
// and the public information about the destination.
// The function generates a random path and a set of random values from exponential distribution.
// Given those values it triggers the encode function, which packs the message into the
// sphinx cryptographic packet format. Next, the encoded packet is combined with a
// flag signaling that this is a usual network packet, and passed to be send.
// The function returns an error if any issues occurred.
func (c *CryptoClient) createSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error) {

	path, err := c.buildPath(recipient)
	if err != nil {
		logLocal.WithError(err).Error("Error in CreateSphinxPacket - generating random path failed")
		return nil, err
	}

	delays, err := c.generateDelaySequence(desiredRateParameter, path.Len())
	if err != nil {
		logLocal.WithError(err).Error("Error in CreateSphinxPacket - generating sequence of delays failed")
		return nil, err
	}

	sphinxPacket, err := sphinx.PackForwardMessage(c.curve, path, delays, message)
	if err != nil {
		logLocal.WithError(err).Error("Error in CreateSphinxPacket - the pack procedure failed")
		return nil, err
	}

	return proto.Marshal(&sphinxPacket)
}

// buildPath builds a path containing the sender's provider,
// a sequence (of length pre-defined in a config file) of randomly
// selected mixes and the recipient's provider
func (c *CryptoClient) buildPath(recipient config.ClientConfig) (config.E2EPath, error) {
	mixSeq, err := c.getRandomMixSequence(c.Network.Mixes, pathLength)
	if err != nil {
		logLocal.WithError(err).Error("Error in buildPath - generating random mix path failed")
		return config.E2EPath{}, err
	}
	path := config.E2EPath{IngressProvider: c.Provider, Mixes: mixSeq, EgressProvider: *recipient.Provider, Recipient: recipient}
	return path, nil
}

// getRandomMixSequence generates a random sequence of given length from all possible mixes.
// If the list of all active mixes is empty or the given length is larger than the set of active mixes,
// an error is returned.
func (c *CryptoClient) getRandomMixSequence(mixes []config.MixConfig, length int) ([]config.MixConfig, error) {
	if len(mixes) == 0 || mixes == nil {
		return nil, errors.New("cannot take a mix sequence from an empty list")
	}
	if length > len(mixes) {
		return mixes, nil
	} else {
		randomSeq, err := helpers.RandomSample(mixes, length)
		if err != nil {
			logLocal.WithError(err).Error("Error in getRandomMixSequence - sampling procedure failed")
			return nil, err
		}
		return randomSeq, nil
	}
}

// generateDelaySequence generates a given length sequence of float64 values. Values are generated
// following the exponential distribution. generateDelaySequence returnes a sequence or an error
// if any of the values could not be generate.
func (c *CryptoClient) generateDelaySequence(desiredRateParameter float64, length int) ([]float64, error) {
	var delays []float64
	for i := 0; i < length; i++ {
		d, err := helpers.RandomExponential(desiredRateParameter)
		if err != nil {
			logLocal.WithError(err).Error("Error in generateDelaySequence - generating random exponential sample failed")
			return nil, err
		}
		delays = append(delays, d)
	}
	return delays, nil
}

// EncodeMessage encodes given message into the Sphinx packet format. EncodeMessage takes as inputs
// the message and the recipient's public configuration.
// EncodeMessage returns the byte representation of the packet or an error if the packet could not be created.
func (c *CryptoClient) EncodeIntoSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error) {

	packet, err := c.createSphinxPacket(message, recipient)
	if err != nil {
		logLocal.WithError(err).Error("Error in EncodeMessage - the pack procedure failed")
		return nil, err
	}
	return packet, err
}

// DecodeMessage decodes the received sphinx packet.
// TODO: this function is finished yet.
func (c *CryptoClient) DecodeSphinxPacket(packet sphinx.SphinxPacket) (sphinx.SphinxPacket, error) {
	return packet, nil
}

func (c *CryptoClient) GetPublicKey() []byte {
	return c.pubKey
}

func NewCryptoClient(id string, pubKey, privKey []byte, curve elliptic.Curve, provider config.MixConfig, network NetworkPKI) *CryptoClient {
	return &CryptoClient{pubKey: pubKey, prvKey: privKey, curve: curve, Provider: provider, Network: network}
}
<|endoftext|>


<commit_before>package shutdown

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

var h = newHandler()

type handler struct {
	Active bool

	mtx  sync.RWMutex
	done chan struct{}
}

func newHandler() *handler {
	h := &handler{done: make(chan struct{})}
	go h.wait()
	return h
}

func IsActive() bool {
	return h.Active
}

func BeforeExit(f func()) {
	h.BeforeExit(f)
}

func (h *handler) BeforeExit(f func()) {
	h.mtx.RLock()
	go func() {
		<-h.done
		f()
		h.mtx.RUnlock()
	}()
}

func Fatal(v ...interface{}) {
	h.Fatal(v)
}

func (h *handler) Fatal(v ...interface{}) {
	h.exit(errors.New(fmt.Sprint(v...)))
}

func (h *handler) wait() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Signal(syscall.SIGTERM))
	<-ch
	h.exit(nil)
}

func (h *handler) exit(err error) {
	h.Active = true
	// signal exit handlers
	close(h.done)
	// wait for exit handlers to finish
	h.mtx.Lock()
	if err != nil {
		log.New(os.Stderr, "", log.Lshortfile|log.Lmicroseconds).Output(3, err.Error())
		os.Exit(1)
	}
	os.Exit(0)
}
<commit_msg>pkg/shutdown: Store handlers in sequence.<commit_after>package shutdown

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

var h = newHandler()

type handler struct {
	Active bool

	mtx   sync.Mutex
	stack []func()
}

func newHandler() *handler {
	h := &handler{}
	go h.wait()
	return h
}

func IsActive() bool {
	return h.Active
}

func BeforeExit(f func()) {
	h.BeforeExit(f)
}

func (h *handler) BeforeExit(f func()) {
	h.mtx.Lock()
	h.stack = append(h.stack, f)
	h.mtx.Unlock()
}

func Fatal(v ...interface{}) {
	h.Fatal(v)
}

func (h *handler) Fatal(v ...interface{}) {
	h.exit(errors.New(fmt.Sprint(v...)))
}

func (h *handler) wait() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Signal(syscall.SIGTERM))
	<-ch
	h.exit(nil)
}

func (h *handler) exit(err error) {
	h.mtx.Lock()
	h.Active = true
	for i := len(h.stack) - 1; i > 0; i-- {
		h.stack[i]()
	}
	if err != nil {
		log.New(os.Stderr, "", log.Lshortfile|log.Lmicroseconds).Output(3, err.Error())
		os.Exit(1)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package log

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/mkideal/log/logger"
)

// Formatter format the data of context
type Formatter interface {
	Format(v interface{}) []byte
}

type JSONFormatter struct{}

var jsonFormatter = JSONFormatter{}

func (f JSONFormatter) Format(v interface{}) []byte {
	b, _ := json.Marshal(v)
	return b
}

// M aliases map
type M map[string]interface{}

// S aliases slice
type S []interface{}

// Context represents a context of logger
type Context interface {
	With(values ...interface{}) ContextLogger
	WithJSON(values ...interface{}) ContextLogger
	SetFormatter(f Formatter) ContextLogger
}

type ContextLogger interface {
	Context
	Trace(format string, args ...interface{}) ContextLogger
	Debug(format string, args ...interface{}) ContextLogger
	Info(format string, args ...interface{}) ContextLogger
	Warn(format string, args ...interface{}) ContextLogger
	Error(format string, args ...interface{}) ContextLogger
	Fatal(format string, args ...interface{}) ContextLogger
}

// contextLogger implements ContextLogger
type contextLogger struct {
	isTrue    bool
	data      interface{}
	formatter Formatter
	b         []byte
}

var bytesTrue = []byte("true")
var bytesFalse = []byte("false")

func toBytes(v interface{}) []byte {
	switch data := v.(type) {
	case string:
		return []byte(data)
	case int:
		return strconv.AppendInt(nil, int64(data), 10)
	case int8:
		return strconv.AppendInt(nil, int64(data), 10)
	case int16:
		return strconv.AppendInt(nil, int64(data), 10)
	case int32:
		return strconv.AppendInt(nil, int64(data), 10)
	case int64:
		return strconv.AppendInt(nil, data, 10)
	case uint:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uintptr:
		return strconv.AppendUint([]byte{'0', 'x'}, uint64(data), 16)
	case uint8:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uint16:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uint32:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uint64:
		return strconv.AppendUint(nil, data, 10)
	case bool:
		if data {
			return bytesTrue
		}
		return bytesFalse
	default:
		buf := new(bytes.Buffer)
		fmt.Fprintf(buf, "%v", data)
		return buf.Bytes()
	}
}

func (l *contextLogger) bytes() []byte {
	if l.b != nil {
		return l.b
	}
	if l.formatter != nil {
		l.b = l.formatter.Format(l.data)
		return l.b
	}
	l.b = toBytes(l.data)
	return l.b
}

func (l *contextLogger) With(values ...interface{}) ContextLogger {
	l.b = nil
	if l.data == nil {
		if len(values) == 0 {
			l.data = values[0]
		} else {
			l.data = values
		}
		return l
	}
	for _, obj := range values {
		l.migrateValue(obj)
	}
	return l
}

func (l *contextLogger) WithJSON(values ...interface{}) ContextLogger {
	return l.With(values...).SetFormatter(jsonFormatter)
}

func (l *contextLogger) migrateValue(v interface{}) {
	m1, ok1 := l.data.(M)
	m2, ok2 := v.(M)
	if ok1 && ok2 {
		for key, val := range m2 {
			m1[key] = val
		}
		l.data = m1
	} else {
		s1, ok1 := l.data.(S)
		s2, ok2 := v.(S)
		if ok1 {
			if ok2 {
				for _, val := range s2 {
					s1 = append(s1, val)
				}
			} else {
				s1 = append(s1, v)
			}
			l.data = s1
		} else {
			if ok2 {
				l.data = append(S{l.data}, s2...)
			} else {
				l.data = S{l.data, v}
			}
		}
	}
}

func (l *contextLogger) SetFormatter(f Formatter) ContextLogger {
	l.formatter = f
	l.b = nil
	return l
}

func (l *contextLogger) formatMessage(format string, args ...interface{}) string {
	buf := bytes.NewBuffer(l.bytes())
	if buf.Len() > 0 && len(format) > 0 {
		buf.WriteString(" | ")
	}
	fmt.Fprintf(buf, format, args...)
	return buf.String()
}

func (l *contextLogger) Trace(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvTRACE {
		if wl, ok := glogger.(logger.WithLogger); ok {
			wl.LogWith(LvTRACE, 1, l.bytes(), format, args...)
		} else {
			glogger.Trace(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Debug(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvDEBUG {
		if wl, ok := glogger.(logger.WithLogger); ok {
			wl.LogWith(LvDEBUG, 1, l.bytes(), format, args...)
		} else {
			glogger.Debug(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Info(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvINFO {
		if wl, ok := glogger.(logger.WithLogger); ok {
			wl.LogWith(LvINFO, 1, l.bytes(), format, args...)
		} else {
			glogger.Info(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Warn(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvWARN {
		if wl, ok := glogger.(logger.WithLogger); ok {
			wl.LogWith(LvWARN, 1, l.bytes(), format, args...)
		} else {
			glogger.Warn(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Error(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvERROR {
		if wl, ok := glogger.(logger.WithLogger); ok {
			wl.LogWith(LvERROR, 1, l.bytes(), format, args...)
		} else {
			glogger.Error(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Fatal(format string, args ...interface{}) ContextLogger {
	if l.isTrue {
		if wl, ok := glogger.(logger.WithLogger); ok {
			wl.LogWith(LvFATAL, 1, l.bytes(), format, args...)
		} else {
			glogger.Fatal(1, l.formatMessage(format, args...))
		}
	}
	return l
}
<commit_msg>fix logger.WithLogger to logger.With<commit_after>package log

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/mkideal/log/logger"
)

// Formatter format the data of context
type Formatter interface {
	Format(v interface{}) []byte
}

type JSONFormatter struct{}

var jsonFormatter = JSONFormatter{}

func (f JSONFormatter) Format(v interface{}) []byte {
	b, _ := json.Marshal(v)
	return b
}

// M aliases map
type M map[string]interface{}

// S aliases slice
type S []interface{}

// Context represents a context of logger
type Context interface {
	With(values ...interface{}) ContextLogger
	WithJSON(values ...interface{}) ContextLogger
	SetFormatter(f Formatter) ContextLogger
}

type ContextLogger interface {
	Context
	Trace(format string, args ...interface{}) ContextLogger
	Debug(format string, args ...interface{}) ContextLogger
	Info(format string, args ...interface{}) ContextLogger
	Warn(format string, args ...interface{}) ContextLogger
	Error(format string, args ...interface{}) ContextLogger
	Fatal(format string, args ...interface{}) ContextLogger
}

// contextLogger implements ContextLogger
type contextLogger struct {
	isTrue    bool
	data      interface{}
	formatter Formatter
	b         []byte
}

var bytesTrue = []byte("true")
var bytesFalse = []byte("false")

func toBytes(v interface{}) []byte {
	switch data := v.(type) {
	case string:
		return []byte(data)
	case int:
		return strconv.AppendInt(nil, int64(data), 10)
	case int8:
		return strconv.AppendInt(nil, int64(data), 10)
	case int16:
		return strconv.AppendInt(nil, int64(data), 10)
	case int32:
		return strconv.AppendInt(nil, int64(data), 10)
	case int64:
		return strconv.AppendInt(nil, data, 10)
	case uint:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uintptr:
		return strconv.AppendUint([]byte{'0', 'x'}, uint64(data), 16)
	case uint8:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uint16:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uint32:
		return strconv.AppendUint(nil, uint64(data), 10)
	case uint64:
		return strconv.AppendUint(nil, data, 10)
	case bool:
		if data {
			return bytesTrue
		}
		return bytesFalse
	default:
		buf := new(bytes.Buffer)
		fmt.Fprintf(buf, "%v", data)
		return buf.Bytes()
	}
}

func (l *contextLogger) bytes() []byte {
	if l.b != nil {
		return l.b
	}
	if l.formatter != nil {
		l.b = l.formatter.Format(l.data)
		return l.b
	}
	l.b = toBytes(l.data)
	return l.b
}

func (l *contextLogger) With(values ...interface{}) ContextLogger {
	l.b = nil
	if l.data == nil {
		if len(values) == 0 {
			l.data = values[0]
		} else {
			l.data = values
		}
		return l
	}
	for _, obj := range values {
		l.migrateValue(obj)
	}
	return l
}

func (l *contextLogger) WithJSON(values ...interface{}) ContextLogger {
	return l.With(values...).SetFormatter(jsonFormatter)
}

func (l *contextLogger) migrateValue(v interface{}) {
	m1, ok1 := l.data.(M)
	m2, ok2 := v.(M)
	if ok1 && ok2 {
		for key, val := range m2 {
			m1[key] = val
		}
		l.data = m1
	} else {
		s1, ok1 := l.data.(S)
		s2, ok2 := v.(S)
		if ok1 {
			if ok2 {
				for _, val := range s2 {
					s1 = append(s1, val)
				}
			} else {
				s1 = append(s1, v)
			}
			l.data = s1
		} else {
			if ok2 {
				l.data = append(S{l.data}, s2...)
			} else {
				l.data = S{l.data, v}
			}
		}
	}
}

func (l *contextLogger) SetFormatter(f Formatter) ContextLogger {
	l.formatter = f
	l.b = nil
	return l
}

func (l *contextLogger) formatMessage(format string, args ...interface{}) string {
	buf := bytes.NewBuffer(l.bytes())
	if buf.Len() > 0 && len(format) > 0 {
		buf.WriteString(" | ")
	}
	fmt.Fprintf(buf, format, args...)
	return buf.String()
}

func (l *contextLogger) Trace(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvTRACE {
		if wl, ok := glogger.(logger.With); ok {
			wl.LogWith(LvTRACE, 1, l.bytes(), format, args...)
		} else {
			glogger.Trace(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Debug(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvDEBUG {
		if wl, ok := glogger.(logger.With); ok {
			wl.LogWith(LvDEBUG, 1, l.bytes(), format, args...)
		} else {
			glogger.Debug(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Info(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvINFO {
		if wl, ok := glogger.(logger.With); ok {
			wl.LogWith(LvINFO, 1, l.bytes(), format, args...)
		} else {
			glogger.Info(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Warn(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvWARN {
		if wl, ok := glogger.(logger.With); ok {
			wl.LogWith(LvWARN, 1, l.bytes(), format, args...)
		} else {
			glogger.Warn(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Error(format string, args ...interface{}) ContextLogger {
	if l.isTrue && glogger.GetLevel() >= LvERROR {
		if wl, ok := glogger.(logger.With); ok {
			wl.LogWith(LvERROR, 1, l.bytes(), format, args...)
		} else {
			glogger.Error(1, l.formatMessage(format, args...))
		}
	}
	return l
}

func (l *contextLogger) Fatal(format string, args ...interface{}) ContextLogger {
	if l.isTrue {
		if wl, ok := glogger.(logger.With); ok {
			wl.LogWith(LvFATAL, 1, l.bytes(), format, args...)
		} else {
			glogger.Fatal(1, l.formatMessage(format, args...))
		}
	}
	return l
}
<|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 main

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"testing"

	"golang.org/x/tools/go/packages/packagestest"
)

func TestBundle(t *testing.T) {
	load := func(name string) string {
		data, err := ioutil.ReadFile(name)
		if err != nil {
			t.Fatal(err)
		}
		return string(data)
	}

	e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
		{
			Name: "initial",
			Files: map[string]interface{}{
				"a.go": load("testdata/src/initial/a.go"),
				"b.go": load("testdata/src/initial/b.go"),
				"c.go": load("testdata/src/initial/c.go"),
			},
		},
		{
			Name: "domain.name/importdecl",
			Files: map[string]interface{}{
				"p.go": load("testdata/src/domain.name/importdecl/p.go"),
			},
		},
	})
	defer e.Cleanup()
	testingOnlyPackagesConfig = e.Config

	os.Args = os.Args[:1] // avoid e.g. -test=short in the output
	out, err := bundle("initial", "github.com/dest", "dest", "prefix", "tag")
	if err != nil {
		t.Fatal(err)
	}

	if got, want := string(out), load("testdata/out.golden"); got != want {
		t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want)

		if err := ioutil.WriteFile("testdata/out.got", out, 0644); err != nil {
			t.Fatal(err)
		}
		t.Log(diff("testdata/out.golden", "testdata/out.got"))
	}
}

func diff(a, b string) string {
	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "plan9":
		cmd = exec.Command("/bin/diff", "-c", a, b)
	default:
		cmd = exec.Command("/usr/bin/diff", "-u", a, b)
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Run() // nonzero exit is expected
	if out.Len() == 0 {
		return "(failed to compute diff)"
	}
	return out.String()
}
<commit_msg>cmd/bundle: expand test coverage to all modes<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 main

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"testing"

	"golang.org/x/tools/go/packages/packagestest"
)

func TestBundle(t *testing.T) { packagestest.TestAll(t, testBundle) }
func testBundle(t *testing.T, x packagestest.Exporter) {
	load := func(name string) string {
		data, err := ioutil.ReadFile(name)
		if err != nil {
			t.Fatal(err)
		}
		return string(data)
	}

	e := packagestest.Export(t, x, []packagestest.Module{
		{
			Name: "initial",
			Files: map[string]interface{}{
				"a.go": load("testdata/src/initial/a.go"),
				"b.go": load("testdata/src/initial/b.go"),
				"c.go": load("testdata/src/initial/c.go"),
			},
		},
		{
			Name: "domain.name/importdecl",
			Files: map[string]interface{}{
				"p.go": load("testdata/src/domain.name/importdecl/p.go"),
			},
		},
	})
	defer e.Cleanup()
	testingOnlyPackagesConfig = e.Config

	os.Args = os.Args[:1] // avoid e.g. -test=short in the output
	out, err := bundle("initial", "github.com/dest", "dest", "prefix", "tag")
	if err != nil {
		t.Fatal(err)
	}

	if got, want := string(out), load("testdata/out.golden"); got != want {
		t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want)

		if err := ioutil.WriteFile("testdata/out.got", out, 0644); err != nil {
			t.Fatal(err)
		}
		t.Log(diff("testdata/out.golden", "testdata/out.got"))
	}
}

func diff(a, b string) string {
	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "plan9":
		cmd = exec.Command("/bin/diff", "-c", a, b)
	default:
		cmd = exec.Command("/usr/bin/diff", "-u", a, b)
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	cmd.Run() // nonzero exit is expected
	if out.Len() == 0 {
		return "(failed to compute diff)"
	}
	return out.String()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/urfave/cli"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

var containerCommand = cli.Command{
	Name:    "container",
	Aliases: []string{"ctr"},
	Subcommands: []cli.Command{
		createContainerCommand,
		startContainerCommand,
		stopContainerCommand,
		removeContainerCommand,
		containerStatusCommand,
		listContainersCommand,
	},
}

var createContainerCommand = cli.Command{
	Name:  "create",
	Usage: "create a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "pod",
			Usage: "the id of the pod sandbox to which the container belongs",
		},
		cli.StringFlag{
			Name:  "config",
			Value: "config.json",
			Usage: "the path of a container config file",
		},
		cli.StringFlag{
			Name:  "name",
			Value: "",
			Usage: "the name of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		if !context.IsSet("pod") {
			return fmt.Errorf("Please specify the id of the pod sandbox to which the container belongs via the --pod option")
		}
		// Test RuntimeServiceClient.CreateContainer
		err = CreateContainer(client, context.String("pod"), context.String("config"), context.String("name"))
		if err != nil {
			return fmt.Errorf("Creating container failed: %v", err)
		}
		return nil
	},
}

var startContainerCommand = cli.Command{
	Name:  "start",
	Usage: "start a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = StartContainer(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Starting the container failed: %v", err)
		}
		return nil
	},
}

var stopContainerCommand = cli.Command{
	Name:  "stop",
	Usage: "stop a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = StopContainer(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Stopping the container failed: %v", err)
		}
		return nil
	},
}

var removeContainerCommand = cli.Command{
	Name:  "remove",
	Usage: "remove a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = RemoveContainer(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Removing the container failed: %v", err)
		}
		return nil
	},
}

var containerStatusCommand = cli.Command{
	Name:  "status",
	Usage: "get the status of a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = ContainerStatus(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Getting the status of the container failed: %v", err)
		}
		return nil
	},
}

var listContainersCommand = cli.Command{
	Name:  "list",
	Usage: "list containers",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "quiet",
			Usage: "list only container IDs",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = ListContainers(client, context.Bool("quiet"))
		if err != nil {
			return fmt.Errorf("listing containers failed: %v", err)
		}
		return nil
	},
}

// CreateContainer sends a CreateContainerRequest to the server, and parses
// the returned CreateContainerResponse.
func CreateContainer(client pb.RuntimeServiceClient, sandbox string, path string, name string) error {
	config, err := loadContainerConfig(path)
	if err != nil {
		return err
	}

	// Override the name by the one specified through CLI
	if name != "" {
		config.Metadata.Name = &name
	}

	r, err := client.CreateContainer(context.Background(), &pb.CreateContainerRequest{
		PodSandboxId: &sandbox,
		Config:       config,
	})
	if err != nil {
		return err
	}
	fmt.Println(*r.ContainerId)
	return nil
}

// StartContainer sends a StartContainerRequest to the server, and parses
// the returned StartContainerResponse.
func StartContainer(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	_, err := client.StartContainer(context.Background(), &pb.StartContainerRequest{
		ContainerId: &ID,
	})
	if err != nil {
		return err
	}
	fmt.Println(ID)
	return nil
}

// StopContainer sends a StopContainerRequest to the server, and parses
// the returned StopContainerResponse.
func StopContainer(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	_, err := client.StopContainer(context.Background(), &pb.StopContainerRequest{
		ContainerId: &ID,
	})
	if err != nil {
		return err
	}
	fmt.Println(ID)
	return nil
}

// RemoveContainer sends a RemoveContainerRequest to the server, and parses
// the returned RemoveContainerResponse.
func RemoveContainer(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	_, err := client.RemoveContainer(context.Background(), &pb.RemoveContainerRequest{
		ContainerId: &ID,
	})
	if err != nil {
		return err
	}
	fmt.Println(ID)
	return nil
}

// ContainerStatus sends a ContainerStatusRequest to the server, and parses
// the returned ContainerStatusResponse.
func ContainerStatus(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	r, err := client.ContainerStatus(context.Background(), &pb.ContainerStatusRequest{
		ContainerId: &ID})
	if err != nil {
		return err
	}
	fmt.Printf("ID: %s\n", *r.Status.Id)
	if r.Status.State != nil {
		fmt.Printf("Status: %s\n", r.Status.State)
	}
	if r.Status.CreatedAt != nil {
		ctm := time.Unix(*r.Status.CreatedAt, 0)
		fmt.Printf("Created: %v\n", ctm)
	}
	if r.Status.StartedAt != nil {
		stm := time.Unix(*r.Status.StartedAt, 0)
		fmt.Printf("Started: %v\n", stm)
	}
	if r.Status.FinishedAt != nil {
		ftm := time.Unix(*r.Status.FinishedAt, 0)
		fmt.Printf("Finished: %v\n", ftm)
	}
	if r.Status.ExitCode != nil {
		fmt.Printf("Exit Code: %v\n", *r.Status.ExitCode)
	}

	return nil
}

// ListContainers sends a ListContainerRequest to the server, and parses
// the returned ListContainerResponse.
func ListContainers(client pb.RuntimeServiceClient, quiet bool) error {
	r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{})
	if err != nil {
		return err
	}
	for _, c := range r.GetContainers() {
		if quiet {
			fmt.Println(*c.Id)
			continue
		}
		fmt.Printf("ID: %s\n", *c.Id)
		fmt.Printf("Pod: %s\n", *c.PodSandboxId)
		if c.State != nil {
			fmt.Printf("Status: %s\n", *c.State)
		}
		if c.CreatedAt != nil {
			ctm := time.Unix(*c.CreatedAt, 0)
			fmt.Printf("Created: %v\n", ctm)
		}
	}
	return nil
}
<commit_msg>Add client support for filtering containers<commit_after>package main

import (
	"fmt"
	"log"
	"time"

	"github.com/urfave/cli"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

var containerCommand = cli.Command{
	Name:    "container",
	Aliases: []string{"ctr"},
	Subcommands: []cli.Command{
		createContainerCommand,
		startContainerCommand,
		stopContainerCommand,
		removeContainerCommand,
		containerStatusCommand,
		listContainersCommand,
	},
}

var createContainerCommand = cli.Command{
	Name:  "create",
	Usage: "create a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "pod",
			Usage: "the id of the pod sandbox to which the container belongs",
		},
		cli.StringFlag{
			Name:  "config",
			Value: "config.json",
			Usage: "the path of a container config file",
		},
		cli.StringFlag{
			Name:  "name",
			Value: "",
			Usage: "the name of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		if !context.IsSet("pod") {
			return fmt.Errorf("Please specify the id of the pod sandbox to which the container belongs via the --pod option")
		}
		// Test RuntimeServiceClient.CreateContainer
		err = CreateContainer(client, context.String("pod"), context.String("config"), context.String("name"))
		if err != nil {
			return fmt.Errorf("Creating container failed: %v", err)
		}
		return nil
	},
}

var startContainerCommand = cli.Command{
	Name:  "start",
	Usage: "start a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = StartContainer(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Starting the container failed: %v", err)
		}
		return nil
	},
}

var stopContainerCommand = cli.Command{
	Name:  "stop",
	Usage: "stop a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = StopContainer(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Stopping the container failed: %v", err)
		}
		return nil
	},
}

var removeContainerCommand = cli.Command{
	Name:  "remove",
	Usage: "remove a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = RemoveContainer(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Removing the container failed: %v", err)
		}
		return nil
	},
}

var containerStatusCommand = cli.Command{
	Name:  "status",
	Usage: "get the status of a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "id of the container",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = ContainerStatus(client, context.String("id"))
		if err != nil {
			return fmt.Errorf("Getting the status of the container failed: %v", err)
		}
		return nil
	},
}

var listContainersCommand = cli.Command{
	Name:  "list",
	Usage: "list containers",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "quiet",
			Usage: "list only container IDs",
		},
		cli.StringFlag{
			Name:  "id",
			Value: "",
			Usage: "filter by container id",
		},
		cli.StringFlag{
			Name:  "pod",
			Value: "",
			Usage: "filter by container pod id",
		},
		cli.StringFlag{
			Name:  "state",
			Value: "",
			Usage: "filter by container state",
		},
	},
	Action: func(context *cli.Context) error {
		// Set up a connection to the server.
		conn, err := getClientConnection(context)
		if err != nil {
			return fmt.Errorf("failed to connect: %v", err)
		}
		defer conn.Close()
		client := pb.NewRuntimeServiceClient(conn)

		err = ListContainers(client, context.Bool("quiet"), context.String("id"), context.String("pod"), context.String("state"))
		if err != nil {
			return fmt.Errorf("listing containers failed: %v", err)
		}
		return nil
	},
}

// CreateContainer sends a CreateContainerRequest to the server, and parses
// the returned CreateContainerResponse.
func CreateContainer(client pb.RuntimeServiceClient, sandbox string, path string, name string) error {
	config, err := loadContainerConfig(path)
	if err != nil {
		return err
	}

	// Override the name by the one specified through CLI
	if name != "" {
		config.Metadata.Name = &name
	}

	r, err := client.CreateContainer(context.Background(), &pb.CreateContainerRequest{
		PodSandboxId: &sandbox,
		Config:       config,
	})
	if err != nil {
		return err
	}
	fmt.Println(*r.ContainerId)
	return nil
}

// StartContainer sends a StartContainerRequest to the server, and parses
// the returned StartContainerResponse.
func StartContainer(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	_, err := client.StartContainer(context.Background(), &pb.StartContainerRequest{
		ContainerId: &ID,
	})
	if err != nil {
		return err
	}
	fmt.Println(ID)
	return nil
}

// StopContainer sends a StopContainerRequest to the server, and parses
// the returned StopContainerResponse.
func StopContainer(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	_, err := client.StopContainer(context.Background(), &pb.StopContainerRequest{
		ContainerId: &ID,
	})
	if err != nil {
		return err
	}
	fmt.Println(ID)
	return nil
}

// RemoveContainer sends a RemoveContainerRequest to the server, and parses
// the returned RemoveContainerResponse.
func RemoveContainer(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	_, err := client.RemoveContainer(context.Background(), &pb.RemoveContainerRequest{
		ContainerId: &ID,
	})
	if err != nil {
		return err
	}
	fmt.Println(ID)
	return nil
}

// ContainerStatus sends a ContainerStatusRequest to the server, and parses
// the returned ContainerStatusResponse.
func ContainerStatus(client pb.RuntimeServiceClient, ID string) error {
	if ID == "" {
		return fmt.Errorf("ID cannot be empty")
	}
	r, err := client.ContainerStatus(context.Background(), &pb.ContainerStatusRequest{
		ContainerId: &ID})
	if err != nil {
		return err
	}
	fmt.Printf("ID: %s\n", *r.Status.Id)
	if r.Status.State != nil {
		fmt.Printf("Status: %s\n", r.Status.State)
	}
	if r.Status.CreatedAt != nil {
		ctm := time.Unix(*r.Status.CreatedAt, 0)
		fmt.Printf("Created: %v\n", ctm)
	}
	if r.Status.StartedAt != nil {
		stm := time.Unix(*r.Status.StartedAt, 0)
		fmt.Printf("Started: %v\n", stm)
	}
	if r.Status.FinishedAt != nil {
		ftm := time.Unix(*r.Status.FinishedAt, 0)
		fmt.Printf("Finished: %v\n", ftm)
	}
	if r.Status.ExitCode != nil {
		fmt.Printf("Exit Code: %v\n", *r.Status.ExitCode)
	}

	return nil
}

// ListContainers sends a ListContainerRequest to the server, and parses
// the returned ListContainerResponse.
func ListContainers(client pb.RuntimeServiceClient, quiet bool, id string, podID string, state string) error {
	filter := &pb.ContainerFilter{}
	if id != "" {
		filter.Id = &id
	}
	if podID != "" {
		filter.PodSandboxId = &podID
	}
	if state != "" {
		st := pb.ContainerState_UNKNOWN
		switch state {
		case "created":
			st = pb.ContainerState_CREATED
			filter.State = &st
		case "running":
			st = pb.ContainerState_RUNNING
			filter.State = &st
		case "stopped":
			st = pb.ContainerState_EXITED
			filter.State = &st
		default:
			log.Fatalf("--state should be one of created, running or stopped")
		}
	}
	r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{
		Filter: filter,
	})
	if err != nil {
		return err
	}
	for _, c := range r.GetContainers() {
		if quiet {
			fmt.Println(*c.Id)
			continue
		}
		fmt.Printf("ID: %s\n", *c.Id)
		fmt.Printf("Pod: %s\n", *c.PodSandboxId)
		if c.State != nil {
			fmt.Printf("Status: %s\n", *c.State)
		}
		if c.CreatedAt != nil {
			ctm := time.Unix(*c.CreatedAt, 0)
			fmt.Printf("Created: %v\n", ctm)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package notmain

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/letsencrypt/boulder/cmd"
	"github.com/letsencrypt/boulder/crl/crl_x509"
	"github.com/letsencrypt/boulder/issuance"
	"github.com/letsencrypt/boulder/linter"
	crlint "github.com/letsencrypt/boulder/linter/lints/crl"
)

func validateShard(url string, issuer *issuance.Certificate) error {
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("downloading crl: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("downloading crl: http status %d", resp.StatusCode)
	}

	crlBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("reading CRL bytes: %w", err)
	}

	crl, err := crl_x509.ParseRevocationList(crlBytes)
	if err != nil {
		return fmt.Errorf("parsing CRL: %w", err)
	}

	err = linter.ProcessResultSet(crlint.LintCRL(crl))
	if err != nil {
		return fmt.Errorf("linting CRL: %w", err)
	}

	err = crl.CheckSignatureFrom(issuer.Certificate)
	if err != nil {
		return fmt.Errorf("checking CRL signature: %w", err)
	}

	return nil
}

func main() {
	urlFile := flag.String("crls", "", "path to a file containing a JSON Array of CRL URLs")
	issuerFile := flag.String("issuer", "", "path to an issuer certificate on disk")
	flag.Parse()

	logger := cmd.NewLogger(cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: -1})

	urlFileContents, err := os.ReadFile(*urlFile)
	cmd.FailOnError(err, "Reading CRL URLs file")

	var urls []string
	err = json.Unmarshal(urlFileContents, &urls)
	cmd.FailOnError(err, "Parsing JSON Array of CRL URLs")

	issuer, err := issuance.LoadCertificate(*issuerFile)
	cmd.FailOnError(err, "Loading issuer certificate")

	errCount := 0
	for _, url := range urls {
		err = validateShard(url, issuer)
		if err != nil {
			errCount += 1
			logger.Errf("CRL %q failed: %s", url, err)
		}
	}

	if errCount != 0 {
		cmd.Fail(fmt.Sprintf("Encountered %d errors", errCount))
	}
	logger.AuditInfo("All CRLs validated")
}

func init() {
	cmd.RegisterCommand("crl-checker", main)
}
<commit_msg>crl-checker: add -emit-revoked flag (#6402)<commit_after>package notmain

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/letsencrypt/boulder/cmd"
	"github.com/letsencrypt/boulder/crl/crl_x509"
	"github.com/letsencrypt/boulder/issuance"
	"github.com/letsencrypt/boulder/linter"
	crlint "github.com/letsencrypt/boulder/linter/lints/crl"
)

func validateShard(url string, issuer *issuance.Certificate) (*crl_x509.RevocationList, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, fmt.Errorf("downloading crl: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("downloading crl: http status %d", resp.StatusCode)
	}

	crlBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading CRL bytes: %w", err)
	}

	crl, err := crl_x509.ParseRevocationList(crlBytes)
	if err != nil {
		return nil, fmt.Errorf("parsing CRL: %w", err)
	}

	err = linter.ProcessResultSet(crlint.LintCRL(crl))
	if err != nil {
		return nil, fmt.Errorf("linting CRL: %w", err)
	}

	err = crl.CheckSignatureFrom(issuer.Certificate)
	if err != nil {
		return nil, fmt.Errorf("checking CRL signature: %w", err)
	}

	return crl, nil
}

func main() {
	urlFile := flag.String("crls", "", "path to a file containing a JSON Array of CRL URLs")
	issuerFile := flag.String("issuer", "", "path to an issuer certificate on disk")
	emitRevoked := flag.Bool("emitRevoked", false, "emit revoked serial numbers on stdout, one per line, hex-encoded")
	flag.Parse()

	logger := cmd.NewLogger(cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: -1})

	urlFileContents, err := os.ReadFile(*urlFile)
	cmd.FailOnError(err, "Reading CRL URLs file")

	var urls []string
	err = json.Unmarshal(urlFileContents, &urls)
	cmd.FailOnError(err, "Parsing JSON Array of CRL URLs")

	issuer, err := issuance.LoadCertificate(*issuerFile)
	cmd.FailOnError(err, "Loading issuer certificate")

	errCount := 0
	for _, url := range urls {
		crl, err := validateShard(url, issuer)
		if err != nil {
			errCount += 1
			logger.Errf("CRL %q failed: %s", url, err)
			continue
		}
		if *emitRevoked {
			for _, c := range crl.RevokedCertificates {
				fmt.Printf("%x\n", c.SerialNumber)
			}
		}
	}

	if errCount != 0 {
		cmd.Fail(fmt.Sprintf("Encountered %d errors", errCount))
	}
	logger.AuditInfo("All CRLs validated")
}

func init() {
	cmd.RegisterCommand("crl-checker", main)
}
<|endoftext|>