<commit_before>package git_pipeline_test

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden/client"
	"github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/concourse/testflight/bosh"
	"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"

	"testing"
	"time"
)

// has ruby, curl
const guidServerRootfs = "/var/vcap/packages/bosh_deployment_resource"

// has git, curl
const gitServerRootfs = "/var/vcap/packages/git_resource"

var flyBin string

var (
	gardenClient garden.Client

	gitServer *gitserver.Server

	successGitServer       *gitserver.Server
	failureGitServer       *gitserver.Server
	noUpdateGitServer      *gitserver.Server
	ensureSuccessGitServer *gitserver.Server
	ensureFailureGitServer *gitserver.Server

	atcURL string
)

type DeploymentTemplateData struct {
	DirectorUUID       string
	GardenLinuxVersion string
}

var _ = BeforeSuite(func() {
	gardenLinuxVersion := os.Getenv("GARDEN_LINUX_VERSION")
	Ω(gardenLinuxVersion).ShouldNot(BeEmpty(), "must set $GARDEN_LINUX_VERSION")

	var err error

	flyBin, err = gexec.Build("github.com/concourse/fly", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	directorUUID := bosh.DirectorUUID()

	bosh.DeleteDeployment("concourse-testflight")

	deploymentData := DeploymentTemplateData{
		DirectorUUID:       directorUUID,
		GardenLinuxVersion: gardenLinuxVersion,
	}

	bosh.Deploy("deployment.yml.tmpl", deploymentData)

	gardenClient = client.New(connection.New("tcp", "10.244.16.2:7777"))
	Eventually(gardenClient.Ping, 10*time.Second).ShouldNot(HaveOccurred())

	guidserver.Start(guidServerRootfs, gardenClient)

	gitServer = gitserver.Start(gitServerRootfs, gardenClient)
	successGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	failureGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	noUpdateGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	ensureSuccessGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	ensureFailureGitServer = gitserver.Start(gitServerRootfs, gardenClient)

	atcURL = "http://10.244.15.2:8080"

	Eventually(errorPolling(atcURL), 1*time.Minute).ShouldNot(HaveOccurred())

	configureCmd := exec.Command(
		flyBin,
		"-t", atcURL,
		"configure",
		"pipeline-name",
		"-c", "pipeline.yml",
		"-v", "failure-git-server="+failureGitServer.URI(),
		"-v", "guid-server-curl-command="+guidserver.CurlCommand(),
		"-v", "no-update-git-server="+noUpdateGitServer.URI(),
		"-v", "origin-git-server="+gitServer.URI(),
		"-v", "success-git-server="+successGitServer.URI(),
		"-v", "ensure-success-git-server="+ensureSuccessGitServer.URI(),
		"-v", "ensure-failure-git-server="+ensureFailureGitServer.URI(),
		"-v", "testflight-helper-image="+guidServerRootfs,
		"--paused=false",
	)

	stdin, err := configureCmd.StdinPipe()
	Ω(err).ShouldNot(HaveOccurred())

	defer stdin.Close()

	configure, err := gexec.Start(configureCmd, GinkgoWriter, GinkgoWriter)
	Ω(err).ShouldNot(HaveOccurred())

	Eventually(configure, 10).Should(gbytes.Say("apply configuration?"))

	fmt.Fprintln(stdin, "y")

	Eventually(configure, 10).Should(gexec.Exit(0))
})

var _ = AfterSuite(func() {
	gitServer.Stop()
	successGitServer.Stop()
	failureGitServer.Stop()
	noUpdateGitServer.Stop()
	ensureSuccessGitServer.Stop()
	ensureFailureGitServer.Stop()

	guidserver.Stop(gardenClient)
})

func TestGitPipeline(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Git Pipeline Suite")
}

func errorPolling(url string) func() error {
	return func() error {
		resp, err := http.Get(url)
		if err == nil {
			resp.Body.Close()
		}

		return err
	}
}
<commit_msg>fix garden client addr<commit_after>package git_pipeline_test

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden/client"
	"github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/concourse/testflight/bosh"
	"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"

	"testing"
	"time"
)

// has ruby, curl
const guidServerRootfs = "/var/vcap/packages/bosh_deployment_resource"

// has git, curl
const gitServerRootfs = "/var/vcap/packages/git_resource"

var flyBin string

var (
	gardenClient garden.Client

	gitServer *gitserver.Server

	successGitServer       *gitserver.Server
	failureGitServer       *gitserver.Server
	noUpdateGitServer      *gitserver.Server
	ensureSuccessGitServer *gitserver.Server
	ensureFailureGitServer *gitserver.Server

	atcURL string
)

type DeploymentTemplateData struct {
	DirectorUUID       string
	GardenLinuxVersion string
}

var _ = BeforeSuite(func() {
	gardenLinuxVersion := os.Getenv("GARDEN_LINUX_VERSION")
	Ω(gardenLinuxVersion).ShouldNot(BeEmpty(), "must set $GARDEN_LINUX_VERSION")

	var err error

	flyBin, err = gexec.Build("github.com/concourse/fly", "-race")
	Ω(err).ShouldNot(HaveOccurred())

	directorUUID := bosh.DirectorUUID()

	bosh.DeleteDeployment("concourse-testflight")

	deploymentData := DeploymentTemplateData{
		DirectorUUID:       directorUUID,
		GardenLinuxVersion: gardenLinuxVersion,
	}

	bosh.Deploy("deployment.yml.tmpl", deploymentData)

	gardenClient = client.New(connection.New("tcp", "10.244.15.2:7777"))
	Eventually(gardenClient.Ping, 10*time.Second).ShouldNot(HaveOccurred())

	guidserver.Start(guidServerRootfs, gardenClient)

	gitServer = gitserver.Start(gitServerRootfs, gardenClient)
	successGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	failureGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	noUpdateGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	ensureSuccessGitServer = gitserver.Start(gitServerRootfs, gardenClient)
	ensureFailureGitServer = gitserver.Start(gitServerRootfs, gardenClient)

	atcURL = "http://10.244.15.2:8080"

	Eventually(errorPolling(atcURL), 1*time.Minute).ShouldNot(HaveOccurred())

	configureCmd := exec.Command(
		flyBin,
		"-t", atcURL,
		"configure",
		"pipeline-name",
		"-c", "pipeline.yml",
		"-v", "failure-git-server="+failureGitServer.URI(),
		"-v", "guid-server-curl-command="+guidserver.CurlCommand(),
		"-v", "no-update-git-server="+noUpdateGitServer.URI(),
		"-v", "origin-git-server="+gitServer.URI(),
		"-v", "success-git-server="+successGitServer.URI(),
		"-v", "ensure-success-git-server="+ensureSuccessGitServer.URI(),
		"-v", "ensure-failure-git-server="+ensureFailureGitServer.URI(),
		"-v", "testflight-helper-image="+guidServerRootfs,
		"--paused=false",
	)

	stdin, err := configureCmd.StdinPipe()
	Ω(err).ShouldNot(HaveOccurred())

	defer stdin.Close()

	configure, err := gexec.Start(configureCmd, GinkgoWriter, GinkgoWriter)
	Ω(err).ShouldNot(HaveOccurred())

	Eventually(configure, 10).Should(gbytes.Say("apply configuration?"))

	fmt.Fprintln(stdin, "y")

	Eventually(configure, 10).Should(gexec.Exit(0))
})

var _ = AfterSuite(func() {
	gitServer.Stop()
	successGitServer.Stop()
	failureGitServer.Stop()
	noUpdateGitServer.Stop()
	ensureSuccessGitServer.Stop()
	ensureFailureGitServer.Stop()

	guidserver.Stop(gardenClient)
})

func TestGitPipeline(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Git Pipeline Suite")
}

func errorPolling(url string) func() error {
	return func() error {
		resp, err := http.Get(url)
		if err == nil {
			resp.Body.Close()
		}

		return err
	}
}
<|endoftext|>


<commit_before>package brokerapi

import (
	"encoding/json"
	"errors"
)

type ServiceBroker interface {
	Services() []Service

	Provision(instanceID string, details ProvisionDetails, asyncAllowed bool) (ProvisionedServiceSpec, error)
	Deprovision(instanceID string, details DeprovisionDetails, asyncAllowed bool) (IsAsync, error)

	Bind(instanceID, bindingID string, details BindDetails) (Binding, error)
	Unbind(instanceID, bindingID string, details UnbindDetails) error

	Update(instanceID string, details UpdateDetails, asyncAllowed bool) (IsAsync, error)

	LastOperation(instanceID string) (LastOperation, error)
}

type IsAsync bool

type ProvisionDetails struct {
	ServiceID        string          `json:"service_id"`
	PlanID           string          `json:"plan_id"`
	OrganizationGUID string          `json:"organization_guid"`
	SpaceGUID        string          `json:"space_guid"`
	RawParameters    json.RawMessage `json:"parameters,omitempty"`
}

type ProvisionedServiceSpec struct {
	IsAsync      bool
	DashboardURL string
}

type BindDetails struct {
	AppGUID      string                 `json:"app_guid"`
	PlanID       string                 `json:"plan_id"`
	ServiceID    string                 `json:"service_id"`
	BindResource *BindResource          `json:"bind_resource,omitempty"`
	Parameters   map[string]interface{} `json:"parameters,omitempty"`
}

type BindResource struct {
	AppGuid string `json:"app_guid,omitempty"`
	Route   string `json:"route,omitempty"`
}

type UnbindDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
}

type DeprovisionDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
}

type UpdateDetails struct {
	ServiceID      string                 `json:"service_id"`
	PlanID         string                 `json:"plan_id"`
	Parameters     map[string]interface{} `json:"parameters"`
	PreviousValues PreviousValues         `json:"previous_values"`
}

type PreviousValues struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
	OrgID     string `json:"organization_id"`
	SpaceID   string `json:"space_id"`
}

type LastOperation struct {
	State       LastOperationState
	Description string
}

type LastOperationState string

const (
	InProgress LastOperationState = "in progress"
	Succeeded  LastOperationState = "succeeded"
	Failed     LastOperationState = "failed"
)

type Binding struct {
	Credentials     interface{} `json:"credentials"`
	SyslogDrainURL  string      `json:"syslog_drain_url,omitempty"`
	RouteServiceURL string      `json:"route_service_url,omitempty"`
}

var (
	ErrInstanceAlreadyExists  = errors.New("instance already exists")
	ErrInstanceDoesNotExist   = errors.New("instance does not exist")
	ErrInstanceLimitMet       = errors.New("instance limit for this service has been reached")
	ErrBindingAlreadyExists   = errors.New("binding already exists")
	ErrBindingDoesNotExist    = errors.New("binding does not exist")
	ErrAsyncRequired          = errors.New("This service plan requires client support for asynchronous service operations.")
	ErrPlanChangeNotSupported = errors.New("The requested plan migration cannot be performed")
	ErrRawParamsInvalid       = errors.New("The format of the parameters is not valid JSON")
)
<commit_msg>Add error message for when service plan quota is reached<commit_after>package brokerapi

import (
	"encoding/json"
	"errors"
)

type ServiceBroker interface {
	Services() []Service

	Provision(instanceID string, details ProvisionDetails, asyncAllowed bool) (ProvisionedServiceSpec, error)
	Deprovision(instanceID string, details DeprovisionDetails, asyncAllowed bool) (IsAsync, error)

	Bind(instanceID, bindingID string, details BindDetails) (Binding, error)
	Unbind(instanceID, bindingID string, details UnbindDetails) error

	Update(instanceID string, details UpdateDetails, asyncAllowed bool) (IsAsync, error)

	LastOperation(instanceID string) (LastOperation, error)
}

type IsAsync bool

type ProvisionDetails struct {
	ServiceID        string          `json:"service_id"`
	PlanID           string          `json:"plan_id"`
	OrganizationGUID string          `json:"organization_guid"`
	SpaceGUID        string          `json:"space_guid"`
	RawParameters    json.RawMessage `json:"parameters,omitempty"`
}

type ProvisionedServiceSpec struct {
	IsAsync      bool
	DashboardURL string
}

type BindDetails struct {
	AppGUID      string                 `json:"app_guid"`
	PlanID       string                 `json:"plan_id"`
	ServiceID    string                 `json:"service_id"`
	BindResource *BindResource          `json:"bind_resource,omitempty"`
	Parameters   map[string]interface{} `json:"parameters,omitempty"`
}

type BindResource struct {
	AppGuid string `json:"app_guid,omitempty"`
	Route   string `json:"route,omitempty"`
}

type UnbindDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
}

type DeprovisionDetails struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
}

type UpdateDetails struct {
	ServiceID      string                 `json:"service_id"`
	PlanID         string                 `json:"plan_id"`
	Parameters     map[string]interface{} `json:"parameters"`
	PreviousValues PreviousValues         `json:"previous_values"`
}

type PreviousValues struct {
	PlanID    string `json:"plan_id"`
	ServiceID string `json:"service_id"`
	OrgID     string `json:"organization_id"`
	SpaceID   string `json:"space_id"`
}

type LastOperation struct {
	State       LastOperationState
	Description string
}

type LastOperationState string

const (
	InProgress LastOperationState = "in progress"
	Succeeded  LastOperationState = "succeeded"
	Failed     LastOperationState = "failed"
)

type Binding struct {
	Credentials     interface{} `json:"credentials"`
	SyslogDrainURL  string      `json:"syslog_drain_url,omitempty"`
	RouteServiceURL string      `json:"route_service_url,omitempty"`
}

var (
	ErrInstanceAlreadyExists  = errors.New("instance already exists")
	ErrInstanceDoesNotExist   = errors.New("instance does not exist")
	ErrInstanceLimitMet       = errors.New("instance limit for this service has been reached")
	ErrPlanQuotaExceeded      = errors.New("The quota for this service plan has been exceeded. Please contact your Operator for help.")
	ErrBindingAlreadyExists   = errors.New("binding already exists")
	ErrBindingDoesNotExist    = errors.New("binding does not exist")
	ErrAsyncRequired          = errors.New("This service plan requires client support for asynchronous service operations.")
	ErrPlanChangeNotSupported = errors.New("The requested plan migration cannot be performed")
	ErrRawParamsInvalid       = errors.New("The format of the parameters is not valid JSON")
)
<|endoftext|>


<commit_before>package endly

import (
	"fmt"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"strings"
)

const DockerServiceId = "docker"
const containerInUse = "is already in use by container"

var dockerErrors = []string{"Error"}
var dockerIgnoreErrors = []string{}

type DockerSystemPathRequest struct {
	SysPath []string
}

type DockerPullRequest struct {
	Target     *Resource
	Repository string
	Tag        string
}

type DockerImagesRequest struct {
	Target     *Resource
	Repository string
	Tag        string
}

type DockerImageInfo struct {
	Repository string
	Tag        string
	ImageId    string
	Size       int
}

type DockerRunRequest struct {
	SysPath []string
	Target     *Resource
	Image      string
	Port       string
	Credential string
	Env        map[string]string
	Mount      map[string]string
	MappedPort map[string]string
	Workdir    string
}

type DockerContainerCheckRequest struct {
	Target *Resource
	Names  string
	Image  string
}

type DockerContainerStartRequest struct {
	Target *Resource
}

type DockerContainerRemoveRequest struct {
	Target *Resource
}

type DockerContainerStopRequest struct {
	Target *Resource
}

type DockerContainerCommandRequest struct {
	Target  *Resource
	Command string
}

type DockerContainerInfo struct {
	ContainerId string
	Image       string
	Command     string
	Status      string
	Port        string
	Names       string
}

type DockerService struct {
	*AbstractService
	SysPath []string
}

func (s *DockerService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "run":
		return &DockerRunRequest{}, nil
	case "syspath":
		return &DockerSystemPathRequest{}, nil
	case "images":
		return &DockerImagesRequest{}, nil
	case "pull":
		return &DockerPullRequest{}, nil
	case "process":
		return &DockerContainerCheckRequest{}, nil
	case "container-command":
		return &DockerContainerCommandRequest{}, nil
	case "container-start":
		return &DockerContainerStartRequest{}, nil
	case "container-stop":
		return &DockerContainerStopRequest{}, nil
	case "container-remove":
		return &DockerContainerStopRequest{}, nil

	}
	return s.AbstractService.NewRequest(action)
}

func (s *DockerService) Run(context *Context, request interface{}) *ServiceResponse {
	var response = &ServiceResponse{Status: "ok"}
	var err error
	switch actualRequest := request.(type) {

	case *DockerSystemPathRequest:
		s.SysPath = actualRequest.SysPath

	case *DockerImagesRequest:
		response.Response, err = s.checkImages(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to check images: %v, %v", actualRequest, err)
		}
	case *DockerPullRequest:
		response.Response, err = s.pullImage(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerCheckRequest:
		response.Response, err = s.checkContainerProcesses(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}

	case *DockerContainerCommandRequest:
		response.Response, err = s.runInContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerStartRequest:
		response.Response, err = s.startContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerStopRequest:
		response.Response, err = s.stopContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerRemoveRequest:
		response.Response, err = s.remoteContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerRunRequest:
		response.Response, err = s.runContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to run: %v(%v), %v", actualRequest.Target.Name, actualRequest.Image, err)
		}

	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

/**
	https://docs.docker.com/compose/reference/run/
Options:
    -d                    Detached mode: Run container in the background, print
                          new container name.
    --name NAME           Assign a name to the container
    --entrypoint CMD      Override the entrypoint of the image.
    -e KEY=VAL            Set an environment variable (can be used multiple times)
    -u, --user=""         Run as specified username or uid
    --no-deps             Don't start linked services.
    --rm                  Remove container after run. Ignored in detached mode.
    -p, --publish=[]      Publish a container's port(s) to the host
    --service-ports       Run command with the service's ports enabled and mapped
                          to the host.
    -v, --volume=[]       Bind mount a volume (default [])
    -T                    Disable pseudo-tty allocation. By default `docker-compose run`
                          allocates a TTY.
    -w, --workdir=""      Working directory inside the container

*/

func (s *DockerService) runContainer(context *Context, request *DockerRunRequest) (*DockerContainerInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v", request.Target.URL)
	}
	if request.Image == "" {
		return nil, fmt.Errorf("Image was empty for %v", request.Target.URL)
	}

	if len(request.SysPath) > 0 {
		s.SysPath = request.SysPath
	}

	var secure = ""
	if request.Credential != "" {
		credential := &storage.PasswordCredential{}
		err := LoadCredential(context.CredentialFile(request.Credential), credential)
		if err != nil {
			return nil, err
		}
		secure = credential.Password
	}
	var args = ""
	for k, v := range request.Env {
		args += fmt.Sprintf("-e %v=%v ", k, context.Expand(v))
	}
	for k, v := range request.Mount {
		args += fmt.Sprintf("-v %v:%v ", context.Expand(k), context.Expand(v))
	}
	for k, v := range request.MappedPort {
		args += fmt.Sprintf("-p %v:%v ", context.Expand(toolbox.AsString(k)), context.Expand(toolbox.AsString(v)))
	}
	if request.Workdir != "" {
		args += fmt.Sprintf("-w %v ", context.Expand(request.Workdir))
	}
	commandInfo, err := s.executeSecureDockerCommand(secure, context, request.Target, dockerIgnoreErrors, "docker run --name %v %v -d %v", request.Target.Name, args, request.Image)
	if err != nil {
		return nil, err
	}
	if strings.Contains(commandInfo.Stdout(), containerInUse) {
		s.stopContainer(context, &DockerContainerStopRequest{Target: request.Target})
		s.remoteContainer(context, &DockerContainerRemoveRequest{Target: request.Target})
		commandInfo, err = s.executeSecureDockerCommand(secure, context, request.Target, dockerErrors, "docker run --name %v %v -d %v", request.Target.Name, args, request.Image)
		if err != nil {
			return nil, err
		}
	}
	return s.checkContainerProcess(context, &DockerContainerCheckRequest{
		Target: request.Target,
		Names:  request.Target.Name,
	})
}

func (s *DockerService) checkContainerProcess(context *Context, request *DockerContainerCheckRequest) (*DockerContainerInfo, error) {
	info, err := s.checkContainerProcesses(context, request)
	if err != nil {
		return nil, err
	}
	if len(info) == 1 {
		return info[0], nil
	}
	return nil, nil
}

func (s *DockerService) startContainer(context *Context, request *DockerContainerStartRequest) (*DockerContainerInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL)
	}
	_, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker start %v", request.Target.Name)
	if err != nil {
		return nil, err
	}
	return s.checkContainerProcess(context, &DockerContainerCheckRequest{
		Target: request.Target,
		Names:  request.Target.Name,
	})

}

func (s *DockerService) stopContainer(context *Context, request *DockerContainerStopRequest) (*DockerContainerInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL)
	}
	info, err := s.checkContainerProcess(context, &DockerContainerCheckRequest{
		Target: request.Target,
		Names:  request.Target.Name,
	})
	if err != nil {
		return nil, err
	}
	if info == nil {
		return nil, err
	}

	_, err = s.executeDockerCommand(context, request.Target, dockerErrors, "docker stop %v", request.Target.Name)
	if err != nil {
		return nil, err
	}

	return info, nil
}

func (s *DockerService) remoteContainer(context *Context, request *DockerContainerRemoveRequest) (*CommandInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL)
	}

	commandInfo, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker rm %v", request.Target.Name)
	if err != nil {
		return nil, err
	}
	return commandInfo, nil
}

func (s *DockerService) runInContainer(context *Context, request *DockerContainerCommandRequest) (*CommandInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL, request.Command)
	}
	return s.executeDockerCommand(context, request.Target, dockerErrors, "docker exec %v /bin/sh -c \"%v\"", request.Target.Name, request.Command)
}

func (s *DockerService) checkContainerProcesses(context *Context, request *DockerContainerCheckRequest) ([]*DockerContainerInfo, error) {
	info, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker ps")
	if err != nil {
		return nil, err
	}
	stdout := info.Stdout()
	var result = make([]*DockerContainerInfo, 0)
	var lines = strings.Split(stdout, "\r\n")
	for i := 1; i < len(lines); i++ {
		columns, ok := ExtractColumns(lines[i])
		if !ok || len(columns) < 7 {
			continue
		}
		var status = "down"
		if strings.Contains(lines[i], "Up") {
			status = "up"
		}
		info := &DockerContainerInfo{
			ContainerId: columns[0],
			Image:       columns[1],
			Command:     columns[2],
			Status:      status,
			Port:        columns[len(columns)-2],
			Names:       columns[len(columns)-1],
		}
		if request.Image != "" && request.Image != info.Image {
			continue
		}
		if request.Names != "" && request.Names != info.Names {
			continue
		}
		result = append(result, info)
	}
	return result, nil
}

func (s *DockerService) pullImage(context *Context, request *DockerPullRequest) (*DockerImageInfo, error) {
	if request.Tag == "" {
		request.Tag = "latest"
	}
	info, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker pull %v:%v", request.Repository, request.Tag)
	if err != nil {
		return nil, err
	}
	stdout := info.Stdout()
	if strings.Contains(stdout, "not found") {
		return nil, fmt.Errorf("Failed to pull docker image,  %v", stdout)
	}
	images, err := s.checkImages(context, &DockerImagesRequest{Target: request.Target, Repository: request.Repository, Tag: request.Tag})
	if err != nil {
		return nil, err
	}
	if len(images) == 1 {
		return images[0], nil
	}
	return nil, fmt.Errorf("Failed to check image status: %v:%v found: %v", request.Repository, request.Tag, len(images))
}

func (s *DockerService) checkImages(context *Context, request *DockerImagesRequest) ([]*DockerImageInfo, error) {
	info, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker images")
	if err != nil {
		return nil, err
	}
	stdout := info.Stdout()
	var result = make([]*DockerImageInfo, 0)
	for _, line := range strings.Split(stdout, "\r\n") {
		columns, ok := ExtractColumns(line)
		if !ok || len(columns) < 4 {
			continue
		}
		var sizeUnit = columns[len(columns)-1]
		var sizeFactor = 1
		switch strings.ToUpper(sizeUnit) {
		case "MB":
			sizeFactor = 1024 * 1024
		case "KB":
			sizeFactor = 1024
		}

		info := &DockerImageInfo{
			Repository: columns[0],
			Tag:        columns[1],
			ImageId:    columns[2],
			Size:       toolbox.AsInt(columns[len(columns)-2]) * sizeFactor,
		}
		if request.Repository != "" {
			if info.Repository != request.Repository {
				continue
			}
		}
		if request.Tag != "" {
			if info.Tag != request.Tag {
				continue
			}
		}
		result = append(result, info)
	}
	return result, nil

}

func (s *DockerService) executeDockerCommand(context *Context, target *Resource, errors []string, template string, arguments ...interface{}) (*CommandInfo, error) {
	return s.executeSecureDockerCommand("", context, target, errors, template, arguments...)
}

func (s *DockerService) executeSecureDockerCommand(secure string, context *Context, target *Resource, errors []string, template string, arguments ...interface{}) (*CommandInfo, error) {
	command := fmt.Sprintf(template, arguments...)
	return context.Execute(target, &ManagedCommand{
		Options: &ExecutionOptions{
			SystemPaths: s.SysPath,
		},
		Executions: []*Execution{
			{
				Secure:  secure,
				Command: command,
				Error:   append(errors, []string{commandNotFound}...),
			},
		},
	})

}

func NewDockerService() Service {
	var result = &DockerService{
		AbstractService: NewAbstractService(DockerServiceId),
	}
	result.AbstractService.Service = result
	return result
}
<commit_msg>patched unsupproted request error handling<commit_after>package endly

import (
	"fmt"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"strings"
)

const DockerServiceId = "docker"
const containerInUse = "is already in use by container"

var dockerErrors = []string{"Error"}
var dockerIgnoreErrors = []string{}

type DockerSystemPathRequest struct {
	SysPath []string
}

type DockerPullRequest struct {
	Target     *Resource
	Repository string
	Tag        string
}

type DockerImagesRequest struct {
	Target     *Resource
	Repository string
	Tag        string
}

type DockerImageInfo struct {
	Repository string
	Tag        string
	ImageId    string
	Size       int
}

type DockerRunRequest struct {
	SysPath []string
	Target     *Resource
	Image      string
	Port       string
	Credential string
	Env        map[string]string
	Mount      map[string]string
	MappedPort map[string]string
	Workdir    string
}

type DockerContainerCheckRequest struct {
	Target *Resource
	Names  string
	Image  string
}

type DockerContainerStartRequest struct {
	Target *Resource
}

type DockerContainerRemoveRequest struct {
	Target *Resource
}

type DockerContainerStopRequest struct {
	Target *Resource
}

type DockerContainerCommandRequest struct {
	Target  *Resource
	Command string
}

type DockerContainerInfo struct {
	ContainerId string
	Image       string
	Command     string
	Status      string
	Port        string
	Names       string
}

type DockerService struct {
	*AbstractService
	SysPath []string
}

func (s *DockerService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "run":
		return &DockerRunRequest{}, nil
	case "syspath":
		return &DockerSystemPathRequest{}, nil
	case "images":
		return &DockerImagesRequest{}, nil
	case "pull":
		return &DockerPullRequest{}, nil
	case "process":
		return &DockerContainerCheckRequest{}, nil
	case "container-command":
		return &DockerContainerCommandRequest{}, nil
	case "container-start":
		return &DockerContainerStartRequest{}, nil
	case "container-stop":
		return &DockerContainerStopRequest{}, nil
	case "container-remove":
		return &DockerContainerStopRequest{}, nil

	}
	return s.AbstractService.NewRequest(action)
}

func (s *DockerService) Run(context *Context, request interface{}) *ServiceResponse {
	var response = &ServiceResponse{Status: "ok"}
	var err error
	switch actualRequest := request.(type) {

	case *DockerSystemPathRequest:
		s.SysPath = actualRequest.SysPath

	case *DockerImagesRequest:
		response.Response, err = s.checkImages(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to check images: %v, %v", actualRequest, err)
		}
	case *DockerPullRequest:
		response.Response, err = s.pullImage(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerCheckRequest:
		response.Response, err = s.checkContainerProcesses(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}

	case *DockerContainerCommandRequest:
		response.Response, err = s.runInContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerStartRequest:
		response.Response, err = s.startContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerStopRequest:
		response.Response, err = s.stopContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerContainerRemoveRequest:
		response.Response, err = s.remoteContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to pull images: %v, %v", actualRequest, err)
		}
	case *DockerRunRequest:
		response.Response, err = s.runContainer(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to run: %v(%v), %v", actualRequest.Target.Name, actualRequest.Image, err)
		}
	default:
		response.Error = fmt.Sprintf("Unsupported request type: %T", request)

	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

/**
	https://docs.docker.com/compose/reference/run/
Options:
    -d                    Detached mode: Run container in the background, print
                          new container name.
    --name NAME           Assign a name to the container
    --entrypoint CMD      Override the entrypoint of the image.
    -e KEY=VAL            Set an environment variable (can be used multiple times)
    -u, --user=""         Run as specified username or uid
    --no-deps             Don't start linked services.
    --rm                  Remove container after run. Ignored in detached mode.
    -p, --publish=[]      Publish a container's port(s) to the host
    --service-ports       Run command with the service's ports enabled and mapped
                          to the host.
    -v, --volume=[]       Bind mount a volume (default [])
    -T                    Disable pseudo-tty allocation. By default `docker-compose run`
                          allocates a TTY.
    -w, --workdir=""      Working directory inside the container

*/

func (s *DockerService) runContainer(context *Context, request *DockerRunRequest) (*DockerContainerInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v", request.Target.URL)
	}
	if request.Image == "" {
		return nil, fmt.Errorf("Image was empty for %v", request.Target.URL)
	}

	if len(request.SysPath) > 0 {
		s.SysPath = request.SysPath
	}

	var secure = ""
	if request.Credential != "" {
		credential := &storage.PasswordCredential{}
		err := LoadCredential(context.CredentialFile(request.Credential), credential)
		if err != nil {
			return nil, err
		}
		secure = credential.Password
	}
	var args = ""
	for k, v := range request.Env {
		args += fmt.Sprintf("-e %v=%v ", k, context.Expand(v))
	}
	for k, v := range request.Mount {
		args += fmt.Sprintf("-v %v:%v ", context.Expand(k), context.Expand(v))
	}
	for k, v := range request.MappedPort {
		args += fmt.Sprintf("-p %v:%v ", context.Expand(toolbox.AsString(k)), context.Expand(toolbox.AsString(v)))
	}
	if request.Workdir != "" {
		args += fmt.Sprintf("-w %v ", context.Expand(request.Workdir))
	}
	commandInfo, err := s.executeSecureDockerCommand(secure, context, request.Target, dockerIgnoreErrors, "docker run --name %v %v -d %v", request.Target.Name, args, request.Image)
	if err != nil {
		return nil, err
	}
	if strings.Contains(commandInfo.Stdout(), containerInUse) {
		s.stopContainer(context, &DockerContainerStopRequest{Target: request.Target})
		s.remoteContainer(context, &DockerContainerRemoveRequest{Target: request.Target})
		commandInfo, err = s.executeSecureDockerCommand(secure, context, request.Target, dockerErrors, "docker run --name %v %v -d %v", request.Target.Name, args, request.Image)
		if err != nil {
			return nil, err
		}
	}
	return s.checkContainerProcess(context, &DockerContainerCheckRequest{
		Target: request.Target,
		Names:  request.Target.Name,
	})
}

func (s *DockerService) checkContainerProcess(context *Context, request *DockerContainerCheckRequest) (*DockerContainerInfo, error) {
	info, err := s.checkContainerProcesses(context, request)
	if err != nil {
		return nil, err
	}
	if len(info) == 1 {
		return info[0], nil
	}
	return nil, nil
}

func (s *DockerService) startContainer(context *Context, request *DockerContainerStartRequest) (*DockerContainerInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL)
	}
	_, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker start %v", request.Target.Name)
	if err != nil {
		return nil, err
	}
	return s.checkContainerProcess(context, &DockerContainerCheckRequest{
		Target: request.Target,
		Names:  request.Target.Name,
	})

}

func (s *DockerService) stopContainer(context *Context, request *DockerContainerStopRequest) (*DockerContainerInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL)
	}
	info, err := s.checkContainerProcess(context, &DockerContainerCheckRequest{
		Target: request.Target,
		Names:  request.Target.Name,
	})
	if err != nil {
		return nil, err
	}
	if info == nil {
		return nil, err
	}

	_, err = s.executeDockerCommand(context, request.Target, dockerErrors, "docker stop %v", request.Target.Name)
	if err != nil {
		return nil, err
	}

	return info, nil
}

func (s *DockerService) remoteContainer(context *Context, request *DockerContainerRemoveRequest) (*CommandInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL)
	}

	commandInfo, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker rm %v", request.Target.Name)
	if err != nil {
		return nil, err
	}
	return commandInfo, nil
}

func (s *DockerService) runInContainer(context *Context, request *DockerContainerCommandRequest) (*CommandInfo, error) {
	if request.Target.Name == "" {
		return nil, fmt.Errorf("Target name was empty for %v and command %v", request.Target.URL, request.Command)
	}
	return s.executeDockerCommand(context, request.Target, dockerErrors, "docker exec %v /bin/sh -c \"%v\"", request.Target.Name, request.Command)
}

func (s *DockerService) checkContainerProcesses(context *Context, request *DockerContainerCheckRequest) ([]*DockerContainerInfo, error) {
	info, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker ps")
	if err != nil {
		return nil, err
	}
	stdout := info.Stdout()
	var result = make([]*DockerContainerInfo, 0)
	var lines = strings.Split(stdout, "\r\n")
	for i := 1; i < len(lines); i++ {
		columns, ok := ExtractColumns(lines[i])
		if !ok || len(columns) < 7 {
			continue
		}
		var status = "down"
		if strings.Contains(lines[i], "Up") {
			status = "up"
		}
		info := &DockerContainerInfo{
			ContainerId: columns[0],
			Image:       columns[1],
			Command:     columns[2],
			Status:      status,
			Port:        columns[len(columns)-2],
			Names:       columns[len(columns)-1],
		}
		if request.Image != "" && request.Image != info.Image {
			continue
		}
		if request.Names != "" && request.Names != info.Names {
			continue
		}
		result = append(result, info)
	}
	return result, nil
}

func (s *DockerService) pullImage(context *Context, request *DockerPullRequest) (*DockerImageInfo, error) {
	if request.Tag == "" {
		request.Tag = "latest"
	}
	info, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker pull %v:%v", request.Repository, request.Tag)
	if err != nil {
		return nil, err
	}
	stdout := info.Stdout()
	if strings.Contains(stdout, "not found") {
		return nil, fmt.Errorf("Failed to pull docker image,  %v", stdout)
	}
	images, err := s.checkImages(context, &DockerImagesRequest{Target: request.Target, Repository: request.Repository, Tag: request.Tag})
	if err != nil {
		return nil, err
	}
	if len(images) == 1 {
		return images[0], nil
	}
	return nil, fmt.Errorf("Failed to check image status: %v:%v found: %v", request.Repository, request.Tag, len(images))
}

func (s *DockerService) checkImages(context *Context, request *DockerImagesRequest) ([]*DockerImageInfo, error) {
	info, err := s.executeDockerCommand(context, request.Target, dockerErrors, "docker images")
	if err != nil {
		return nil, err
	}
	stdout := info.Stdout()
	var result = make([]*DockerImageInfo, 0)
	for _, line := range strings.Split(stdout, "\r\n") {
		columns, ok := ExtractColumns(line)
		if !ok || len(columns) < 4 {
			continue
		}
		var sizeUnit = columns[len(columns)-1]
		var sizeFactor = 1
		switch strings.ToUpper(sizeUnit) {
		case "MB":
			sizeFactor = 1024 * 1024
		case "KB":
			sizeFactor = 1024
		}

		info := &DockerImageInfo{
			Repository: columns[0],
			Tag:        columns[1],
			ImageId:    columns[2],
			Size:       toolbox.AsInt(columns[len(columns)-2]) * sizeFactor,
		}
		if request.Repository != "" {
			if info.Repository != request.Repository {
				continue
			}
		}
		if request.Tag != "" {
			if info.Tag != request.Tag {
				continue
			}
		}
		result = append(result, info)
	}
	return result, nil

}

func (s *DockerService) executeDockerCommand(context *Context, target *Resource, errors []string, template string, arguments ...interface{}) (*CommandInfo, error) {
	return s.executeSecureDockerCommand("", context, target, errors, template, arguments...)
}

func (s *DockerService) executeSecureDockerCommand(secure string, context *Context, target *Resource, errors []string, template string, arguments ...interface{}) (*CommandInfo, error) {
	command := fmt.Sprintf(template, arguments...)
	return context.Execute(target, &ManagedCommand{
		Options: &ExecutionOptions{
			SystemPaths: s.SysPath,
		},
		Executions: []*Execution{
			{
				Secure:  secure,
				Command: command,
				Error:   append(errors, []string{commandNotFound}...),
			},
		},
	})

}

func NewDockerService() Service {
	var result = &DockerService{
		AbstractService: NewAbstractService(DockerServiceId),
	}
	result.AbstractService.Service = result
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"

	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"sort"
	"time"
)

// Handler for bash -c exec command.
func handler(w http.ResponseWriter, r *http.Request) {
	type ShellRequest struct {
		Command string
	}
	type ShellResponse struct {
		Stdin, Stdout, Stderr, Code string
	}

	var req ShellRequest
	var res ShellResponse

	decoder := json.NewDecoder(r.Body)
	encoder := json.NewEncoder(w)
	if err := decoder.Decode(&req); err != nil || req.Command == "" {
		fmt.Fprintf(w, "Unable to parse param 'command': %s\n", err)
		return
	}

	// Execute the command
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("bash", "-c", req.Command)
	cmd.Stdout, cmd.Stderr = &stdout, &stderr
	if err := cmd.Run(); err != nil {
		res.Code = fmt.Sprintf("%s", err)
	}

	res.Stdin = req.Command
	res.Stdout = stdout.String()
	res.Stderr = stderr.String()
	encoder.Encode(&res)
}

// Start a service proxy.
func (cli *ServicedCli) CmdProxy(args ...string) error {

	if err := proxyCmd.Parse(args); err != nil {
		return err
	}
	if len(proxyCmd.Args()) != 2 {
		proxyCmd.Usage()
		glog.Flush()
		os.Exit(2)
	}
	config := serviced.MuxConfig{}
	config.TCPMux.Port = proxyOptions.muxport
	config.TCPMux.Enabled = proxyOptions.mux
	config.TCPMux.UseTLS = proxyOptions.tls
	config.ServiceId = proxyCmd.Arg(0)
	config.Command = proxyCmd.Arg(1)

	if config.TCPMux.Enabled {
		go config.TCPMux.ListenAndMux()
	}

	http.HandleFunc("/exec", handler)
	http.ListenAndServe(":50000", nil)

	procexit := make(chan int)

	// continually execute subprocess
	go func(cmdString string) {
		defer func() { procexit <- 1 }()
		for {
			glog.V(0).Info("About to execute: ", cmdString)
			cmd := exec.Command("bash", "-c", cmdString)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Stdin = os.Stdin
			err := cmd.Run()
			if err != nil {
				glog.Errorf("Problem running service: %v", err)
				glog.Flush()
			}
			if !proxyOptions.autorestart {
				break
			}
			glog.V(0).Info("service exited, sleeping...")
			time.Sleep(time.Minute)
		}
	}(config.Command)

	go func() {
		for {
			func() {
				client, err := serviced.NewLBClient(proxyOptions.servicedEndpoint)
				if err != nil {
					glog.Errorf("Could not create a client to endpoint %s: %s", proxyOptions.servicedEndpoint, err)
					return
				}
				defer client.Close()

				var endpoints map[string][]*dao.ApplicationEndpoint
				err = client.GetServiceEndpoints(config.ServiceId, &endpoints)
				if err != nil {
					glog.Errorf("Error getting application endpoints for service %s: %s", config.ServiceId, err)
					return
				}

				for key, endpointList := range endpoints {
					if len(endpointList) <= 0 {
						glog.Warningf("No endpoints found for %s", key)
						if proxy, ok := proxies[key]; ok {
							emptyAddressList := make([]string, 0)
							proxy.SetNewAddresses(emptyAddressList)
						}
						continue
					}
					addresses := make([]string, len(endpointList))
					for i, endpoint := range endpointList {
						addresses[i] = fmt.Sprintf("%s:%d", endpoint.HostIp, endpoint.HostPort)
					}
					sort.Strings(addresses)

					var proxy *serviced.Proxy
					var ok bool
					if proxy, ok = proxies[key]; !ok {
						// setup a new proxy
						listener, err := net.Listen("tcp4", fmt.Sprintf(":%d", endpointList[0].ContainerPort))
						if err != nil {
							glog.Errorf("Could not bind to port: %s", err)
							continue
						}
						proxy, err = serviced.NewProxy(
							fmt.Sprintf("%v", endpointList[0]),
							uint16(config.TCPMux.Port),
							config.TCPMux.UseTLS,
							listener)
						if err != nil {
							glog.Errorf("Could not build proxy %s", err)
							continue
						}
						proxies[key] = proxy
					}
					proxy.SetNewAddresses(addresses)
				}
			}()

			time.Sleep(time.Second * 10)
		}
	}()

	<-procexit // Wait for proc goroutine to exit

	glog.Flush()
	os.Exit(0)
	return nil
}

var proxies map[string]*serviced.Proxy

func init() {
	proxies = make(map[string]*serviced.Proxy)
}
<commit_msg>start webserver in go routine to avoid block main()<commit_after>package main

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"

	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"sort"
	"time"
)

// Handler for bash -c exec command.
func handler(w http.ResponseWriter, r *http.Request) {
	type ShellRequest struct {
		Command string
	}
	type ShellResponse struct {
		Stdin, Stdout, Stderr, Code string
	}

	var req ShellRequest
	var res ShellResponse

	decoder := json.NewDecoder(r.Body)
	encoder := json.NewEncoder(w)
	if err := decoder.Decode(&req); err != nil || req.Command == "" {
		fmt.Fprintf(w, "Unable to parse param 'command': %s\n", err)
		return
	}

	// Execute the command
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("bash", "-c", req.Command)
	cmd.Stdout, cmd.Stderr = &stdout, &stderr
	if err := cmd.Run(); err != nil {
		res.Code = fmt.Sprintf("%s", err)
	}

	res.Stdin = req.Command
	res.Stdout = stdout.String()
	res.Stderr = stderr.String()
	encoder.Encode(&res)
}

// Start a service proxy.
func (cli *ServicedCli) CmdProxy(args ...string) error {

	if err := proxyCmd.Parse(args); err != nil {
		return err
	}
	if len(proxyCmd.Args()) != 2 {
		proxyCmd.Usage()
		glog.Flush()
		os.Exit(2)
	}
	config := serviced.MuxConfig{}
	config.TCPMux.Port = proxyOptions.muxport
	config.TCPMux.Enabled = proxyOptions.mux
	config.TCPMux.UseTLS = proxyOptions.tls
	config.ServiceId = proxyCmd.Arg(0)
	config.Command = proxyCmd.Arg(1)

	if config.TCPMux.Enabled {
		go config.TCPMux.ListenAndMux()
	}

	http.HandleFunc("/exec", handler)
	go http.ListenAndServe(":50000", nil)

	procexit := make(chan int)

	// continually execute subprocess
	go func(cmdString string) {
		defer func() { procexit <- 1 }()
		for {
			glog.V(0).Info("About to execute: ", cmdString)
			cmd := exec.Command("bash", "-c", cmdString)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Stdin = os.Stdin
			err := cmd.Run()
			if err != nil {
				glog.Errorf("Problem running service: %v", err)
				glog.Flush()
			}
			if !proxyOptions.autorestart {
				break
			}
			glog.V(0).Info("service exited, sleeping...")
			time.Sleep(time.Minute)
		}
	}(config.Command)

	go func() {
		for {
			func() {
				client, err := serviced.NewLBClient(proxyOptions.servicedEndpoint)
				if err != nil {
					glog.Errorf("Could not create a client to endpoint %s: %s", proxyOptions.servicedEndpoint, err)
					return
				}
				defer client.Close()

				var endpoints map[string][]*dao.ApplicationEndpoint
				err = client.GetServiceEndpoints(config.ServiceId, &endpoints)
				if err != nil {
					glog.Errorf("Error getting application endpoints for service %s: %s", config.ServiceId, err)
					return
				}

				for key, endpointList := range endpoints {
					if len(endpointList) <= 0 {
						glog.Warningf("No endpoints found for %s", key)
						if proxy, ok := proxies[key]; ok {
							emptyAddressList := make([]string, 0)
							proxy.SetNewAddresses(emptyAddressList)
						}
						continue
					}
					addresses := make([]string, len(endpointList))
					for i, endpoint := range endpointList {
						addresses[i] = fmt.Sprintf("%s:%d", endpoint.HostIp, endpoint.HostPort)
					}
					sort.Strings(addresses)

					var proxy *serviced.Proxy
					var ok bool
					if proxy, ok = proxies[key]; !ok {
						// setup a new proxy
						listener, err := net.Listen("tcp4", fmt.Sprintf(":%d", endpointList[0].ContainerPort))
						if err != nil {
							glog.Errorf("Could not bind to port: %s", err)
							continue
						}
						proxy, err = serviced.NewProxy(
							fmt.Sprintf("%v", endpointList[0]),
							uint16(config.TCPMux.Port),
							config.TCPMux.UseTLS,
							listener)
						if err != nil {
							glog.Errorf("Could not build proxy %s", err)
							continue
						}
						proxies[key] = proxy
					}
					proxy.SetNewAddresses(addresses)
				}
			}()

			time.Sleep(time.Second * 10)
		}
	}()

	<-procexit // Wait for proc goroutine to exit

	glog.Flush()
	os.Exit(0)
	return nil
}

var proxies map[string]*serviced.Proxy

func init() {
	proxies = make(map[string]*serviced.Proxy)
}
<|endoftext|>


<commit_before>package appimport

import (
	"path/filepath"
	"testing"

	"strings"

	"os"

	"log"

	"github.com/drud/ddev/pkg/testcommon"
	"github.com/drud/ddev/pkg/util"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/stretchr/testify/assert"
)

// TestValidateAsset tests validation of asset paths.
func TestValidateAsset(t *testing.T) {
	assert := assert.New(t)

	testArchivePath := filepath.Join(testcommon.CreateTmpDir("appimport"), "db.tar.gz")

	testFile, err := os.Create(testArchivePath)
	if err != nil {
		log.Fatalf("failed to create dummy test file: %v", err)
	}
	err = testFile.Close()
	if err != nil {
		log.Fatalf("failed to create dummy test file: %v", err)
	}

	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("failed to get cwd: %s", err)
	}

	// test tilde expansion
	userDir, err := homedir.Dir()
	testDir := filepath.Join(userDir, "testpath")
	assert.NoError(err)
	err = os.Mkdir(testDir, 0755)
	assert.NoError(err)

	testPath, err := ValidateAsset("~/testpath", "files")
	assert.NoError(err)
	assert.Contains(testPath, userDir)
	assert.False(strings.Contains(testPath, "~"))
	err = os.Remove(testDir)
	assert.NoError(err)

	// test a relative path
	testPath, err = ValidateAsset("../../vendor", "files")
	assert.NoError(err)
	upTwo := strings.TrimSuffix(cwd, "/pkg/appimport")
	assert.Contains(testPath, upTwo)

	// archive
	_, err = ValidateAsset(testArchivePath, "db")
	assert.Error(err)
	assert.Equal(err.Error(), "is archive")

	// db no sql
	_, err = ValidateAsset("appimport.go", "db")
	assert.Contains(err.Error(), "provided path is not a .sql file or archive")
	assert.Error(err)

	// files not a directory
	_, err = ValidateAsset("appimport.go", "files")
	assert.Error(err)
	assert.Contains(err.Error(), "provided path is not a directory or archive")

	err = os.RemoveAll(filepath.Dir(testArchivePath))
	util.CheckErr(err)
}
<commit_msg>use unique folder for testing ValidateAsset, fixes #203 (#338)<commit_after>package appimport

import (
	"path/filepath"
	"testing"

	"strings"

	"os"

	"log"

	"github.com/drud/ddev/pkg/testcommon"
	"github.com/drud/ddev/pkg/util"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/stretchr/testify/assert"
)

// TestValidateAsset tests validation of asset paths.
func TestValidateAsset(t *testing.T) {
	assert := assert.New(t)

	testArchivePath := filepath.Join(testcommon.CreateTmpDir("appimport"), "db.tar.gz")

	testFile, err := os.Create(testArchivePath)
	if err != nil {
		log.Fatalf("failed to create dummy test file: %v", err)
	}
	err = testFile.Close()
	if err != nil {
		log.Fatalf("failed to create dummy test file: %v", err)
	}

	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("failed to get cwd: %s", err)
	}

	// test tilde expansion
	userDir, err := homedir.Dir()
	testDirName := "tmp.ddev.testpath-" + util.RandString(4)
	testDir := filepath.Join(userDir, testDirName)
	assert.NoError(err)
	err = os.Mkdir(testDir, 0755)
	assert.NoError(err)

	testPath, err := ValidateAsset("~/"+testDirName, "files")
	assert.NoError(err)
	assert.Contains(testPath, userDir)
	assert.False(strings.Contains(testPath, "~"))
	err = os.Remove(testDir)
	assert.NoError(err)

	// test a relative path
	testPath, err = ValidateAsset("../../vendor", "files")
	assert.NoError(err)
	upTwo := strings.TrimSuffix(cwd, "/pkg/appimport")
	assert.Contains(testPath, upTwo)

	// archive
	_, err = ValidateAsset(testArchivePath, "db")
	assert.Error(err)
	assert.Equal(err.Error(), "is archive")

	// db no sql
	_, err = ValidateAsset("appimport.go", "db")
	assert.Contains(err.Error(), "provided path is not a .sql file or archive")
	assert.Error(err)

	// files not a directory
	_, err = ValidateAsset("appimport.go", "files")
	assert.Error(err)
	assert.Contains(err.Error(), "provided path is not a directory or archive")

	err = os.RemoveAll(filepath.Dir(testArchivePath))
	util.CheckErr(err)
}
<|endoftext|>


<commit_before>// Package internal holds asset templates used by bootkube.
package internal

var (
	KubeConfigTemplate = []byte(`apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    server: {{ .Server }}
    certificate-authority-data: {{ .CACert }}
users:
- name: kubelet
  user:
    client-certificate-data: {{ .KubeletCert}}
    client-key-data: {{ .KubeletKey }}
contexts:
- context:
    cluster: local
    user: kubelet
`)
	KubeletTemplate = []byte(`apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kubelet
  namespace: kube-system
  labels:
    k8s-app: kubelet
spec:
  template:
    metadata:
      labels:
        k8s-app: kubelet
    spec:
      containers:
      - name: kubelet
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - /nsenter
        - --target=1
        - --mount
        - --wd=.
        - --
        - ./hyperkube
        - kubelet
        - --pod-manifest-path=/etc/kubernetes/manifests
        - --allow-privileged
        - --hostname-override=$(MY_POD_IP)
        - --cluster-dns=10.3.0.10
        - --cluster-domain=cluster.local
        - --kubeconfig=/etc/kubernetes/kubeconfig
        - --require-kubeconfig
        - --lock-file=/var/run/lock/kubelet.lock
        env:
          - name: MY_POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        securityContext:
          privileged: true
        volumeMounts:
        - name: dev
          mountPath: /dev
        - name: run
          mountPath: /run
        - name: sys
          mountPath: /sys
          readOnly: true
        - name: etc-kubernetes
          mountPath: /etc/kubernetes
          readOnly: true
        - name: etc-ssl-certs
          mountPath: /etc/ssl/certs
          readOnly: true
        - name: var-lib-docker
          mountPath: /var/lib/docker
        - name: var-lib-kubelet
          mountPath: /var/lib/kubelet
        - name: var-lib-rkt
          mountPath: /var/lib/rkt
      hostNetwork: true
      hostPID: true
      volumes:
      - name: dev
        hostPath:
          path: /dev
      - name: run
        hostPath:
          path: /run
      - name: sys
        hostPath:
          path: /sys
      - name: etc-kubernetes
        hostPath:
          path: /etc/kubernetes
      - name: etc-ssl-certs
        hostPath:
          path: /usr/share/ca-certificates
      - name: var-lib-docker
        hostPath:
          path: /var/lib/docker
      - name: var-lib-kubelet
        hostPath:
          path: /var/lib/kubelet
      - name: var-lib-rkt
        hostPath:
          path: /var/lib/rkt
`)
	APIServerTemplate = []byte(`apiVersion: "extensions/v1beta1"
kind: DaemonSet
metadata:
  name: kube-apiserver
  namespace: kube-system
  labels:
    k8s-app: kube-apiserver
spec:
  template:
    metadata:
      labels:
        k8s-app: kube-apiserver
    spec:
      nodeSelector:
        master: "true"
      hostNetwork: true
      containers:
      - name: kube-apiserver
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - /hyperkube
        - apiserver
        - --bind-address=0.0.0.0
        - --secure-port=443
        - --insecure-port=8080
        - --advertise-address=$(MY_POD_IP)
        - --etcd-servers={{ range $i, $e := .EtcdServers }}{{ if $i }},{{end}}{{ $e }}{{end}}
        - --storage-backend={{.StorageBackend}}
        - --allow-privileged=true
        - --service-cluster-ip-range=10.3.0.0/24
        - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota
        - --runtime-config=api/all=true
        - --tls-cert-file=/etc/kubernetes/secrets/apiserver.crt
        - --tls-private-key-file=/etc/kubernetes/secrets/apiserver.key
        - --service-account-key-file=/etc/kubernetes/secrets/service-account.pub
        - --client-ca-file=/etc/kubernetes/secrets/ca.crt
        - --cloud-provider={{ .CloudProvider  }}
        env:
          - name: MY_POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
        - mountPath: /etc/kubernetes/secrets
          name: secrets
          readOnly: true
      volumes:
      - name: ssl-certs-host
        hostPath:
          path: /usr/share/ca-certificates
      - name: secrets
        secret:
          secretName: kube-apiserver
`)
	CheckpointerTemplate = []byte(`apiVersion: "extensions/v1beta1"
kind: DaemonSet
metadata:
  name: checkpoint-installer
  namespace: kube-system
  labels:
    k8s-app: kube-api-checkpointer
spec:
  template:
    metadata:
      labels:
        k8s-app: kube-api-checkpointer
    spec:
      nodeSelector:
        master: "true"
      hostNetwork: true
      containers:
      - name: checkpoint-installer
        image: quay.io/coreos/pod-checkpointer:808d9e50c8beaa82672cb7dbd57a782d7d6c0262
        command:
        - /checkpoint-installer.sh
        volumeMounts:
        - mountPath: /etc/kubernetes/manifests
          name: etc-k8s-manifests
      volumes:
      - name: etc-k8s-manifests
        hostPath:
          path: /etc/kubernetes/manifests
`)
	ControllerManagerTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-controller-manager
  namespace: kube-system
  labels:
    k8s-app: kube-controller-manager
spec:
  replicas: 2
  template:
    metadata:
      labels:
        k8s-app: kube-controller-manager
    spec:
      containers:
      - name: kube-controller-manager
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - ./hyperkube
        - controller-manager
        - --root-ca-file=/etc/kubernetes/secrets/ca.crt
        - --service-account-private-key-file=/etc/kubernetes/secrets/service-account.key
        - --leader-elect=true
        - --cloud-provider={{ .CloudProvider  }}
        - --configure-cloud-routes=false
        volumeMounts:
        - name: secrets
          mountPath: /etc/kubernetes/secrets
          readOnly: true
        - name: ssl-host
          mountPath: /etc/ssl/certs
          readOnly: true
      volumes:
      - name: secrets
        secret:
          secretName: kube-controller-manager
      - name: ssl-host
        hostPath:
          path: /usr/share/ca-certificates
      dnsPolicy: Default # Don't use cluster DNS.
`)
	SchedulerTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-scheduler
  namespace: kube-system
  labels:
    k8s-app: kube-scheduler
spec:
  replicas: 2
  template:
    metadata:
      labels:
        k8s-app: kube-scheduler
    spec:
      containers:
      - name: kube-scheduler
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - ./hyperkube
        - scheduler
        - --leader-elect=true
`)
	ProxyTemplate = []byte(`apiVersion: "extensions/v1beta1"
kind: DaemonSet
metadata:
  name: kube-proxy
  namespace: kube-system
  labels:
    k8s_app: kube-proxy
spec:
  template:
    metadata:
      labels:
        k8s_app: kube-proxy
    spec:
      hostNetwork: true
      containers:
      - name: kube-proxy
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - /hyperkube
        - proxy
        - --kubeconfig=/etc/kubernetes/kubeconfig
        - --proxy-mode=iptables
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
        - name: etc-kubernetes
          mountPath: /etc/kubernetes
          readOnly: true
      volumes:
      - hostPath:
          path: /usr/share/ca-certificates
        name: ssl-certs-host
      - name: etc-kubernetes
        hostPath:
          path: /etc/kubernetes
`)
	DNSDeploymentTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: kube-dns
  template:
    metadata:
      labels:
        k8s-app: kube-dns
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ''
        scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
    spec:
      containers:
      - name: kubedns
        image: gcr.io/google_containers/kubedns-amd64:1.8
        resources:
          # TODO: Set memory limits when we've profiled the container for large
          # clusters, then set request = limit to keep this container in
          # guaranteed class. Currently, this container falls into the
          # "burstable" category so the kubelet doesn't backoff from restarting it.
          limits:
            memory: 170Mi
          requests:
            cpu: 100m
            memory: 70Mi
        livenessProbe:
          httpGet:
            path: /healthz-kubedns
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /readiness
            port: 8081
            scheme: HTTP
          # we poll on pod startup for the Kubernetes master service and
          # only setup the /readiness HTTP server once that's available.
          initialDelaySeconds: 3
          timeoutSeconds: 5
        args:
        # command = "/kube-dns"
        - --domain=cluster.local.
        - --dns-port=10053
        ports:
        - containerPort: 10053
          name: dns-local
          protocol: UDP
        - containerPort: 10053
          name: dns-tcp-local
          protocol: TCP
      - name: dnsmasq
        image: gcr.io/google_containers/kube-dnsmasq-amd64:1.4
        livenessProbe:
          httpGet:
            path: /healthz-dnsmasq
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        args:
        - --cache-size=1000
        - --no-resolv
        - --server=127.0.0.1#10053
        - --log-facility=-
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
      - name: healthz
        image: gcr.io/google_containers/exechealthz-amd64:1.2
        resources:
          limits:
            memory: 50Mi
          requests:
            cpu: 10m
            # Note that this container shouldn't really need 50Mi of memory. The
            # limits are set higher than expected pending investigation on #29688.
            # The extra memory was stolen from the kubedns container to keep the
            # net memory requested by the pod constant.
            memory: 50Mi
        args:
        - --cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 >/dev/null
        - --url=/healthz-dnsmasq
        - --cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1:10053 >/dev/null
        - --url=/healthz-kubedns
        - --port=8080
        - --quiet
        ports:
        - containerPort: 8080
          protocol: TCP
      dnsPolicy: Default  # Don't use cluster DNS.
`)
	DNSSvcTemplate = []byte(`apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "KubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP:  10.3.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP
`)
	EtcdOperatorTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: etcd-operator
  namespace: kube-system
  labels:
    k8s-app: etcd-operator
spec:
  replicas: 1
  template:
    metadata:
      labels:
        k8s-app: etcd-operator
    spec:
      containers:
      - name: etcd-operator
        image: quay.io/coreos/etcd-operator
        env:
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
`)
	EtcdSvcTemplate = []byte(`apiVersion: v1
kind: Service
metadata:
  name: etcd-service
  namespace: kube-system
spec:
  selector:
    app: etcd
    etcd_cluster: etcd-cluster
  clusterIP: 10.3.0.15
  ports:
  - name: client
    port: 2379
    protocol: TCP
`)
)
<commit_msg>Set proxy hostname and cluster cidr<commit_after>// Package internal holds asset templates used by bootkube.
package internal

var (
	KubeConfigTemplate = []byte(`apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    server: {{ .Server }}
    certificate-authority-data: {{ .CACert }}
users:
- name: kubelet
  user:
    client-certificate-data: {{ .KubeletCert}}
    client-key-data: {{ .KubeletKey }}
contexts:
- context:
    cluster: local
    user: kubelet
`)
	KubeletTemplate = []byte(`apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kubelet
  namespace: kube-system
  labels:
    k8s-app: kubelet
spec:
  template:
    metadata:
      labels:
        k8s-app: kubelet
    spec:
      containers:
      - name: kubelet
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - /nsenter
        - --target=1
        - --mount
        - --wd=.
        - --
        - ./hyperkube
        - kubelet
        - --pod-manifest-path=/etc/kubernetes/manifests
        - --allow-privileged
        - --hostname-override=$(MY_POD_IP)
        - --cluster-dns=10.3.0.10
        - --cluster-domain=cluster.local
        - --kubeconfig=/etc/kubernetes/kubeconfig
        - --require-kubeconfig
        - --lock-file=/var/run/lock/kubelet.lock
        env:
          - name: MY_POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        securityContext:
          privileged: true
        volumeMounts:
        - name: dev
          mountPath: /dev
        - name: run
          mountPath: /run
        - name: sys
          mountPath: /sys
          readOnly: true
        - name: etc-kubernetes
          mountPath: /etc/kubernetes
          readOnly: true
        - name: etc-ssl-certs
          mountPath: /etc/ssl/certs
          readOnly: true
        - name: var-lib-docker
          mountPath: /var/lib/docker
        - name: var-lib-kubelet
          mountPath: /var/lib/kubelet
        - name: var-lib-rkt
          mountPath: /var/lib/rkt
      hostNetwork: true
      hostPID: true
      volumes:
      - name: dev
        hostPath:
          path: /dev
      - name: run
        hostPath:
          path: /run
      - name: sys
        hostPath:
          path: /sys
      - name: etc-kubernetes
        hostPath:
          path: /etc/kubernetes
      - name: etc-ssl-certs
        hostPath:
          path: /usr/share/ca-certificates
      - name: var-lib-docker
        hostPath:
          path: /var/lib/docker
      - name: var-lib-kubelet
        hostPath:
          path: /var/lib/kubelet
      - name: var-lib-rkt
        hostPath:
          path: /var/lib/rkt
`)
	APIServerTemplate = []byte(`apiVersion: "extensions/v1beta1"
kind: DaemonSet
metadata:
  name: kube-apiserver
  namespace: kube-system
  labels:
    k8s-app: kube-apiserver
spec:
  template:
    metadata:
      labels:
        k8s-app: kube-apiserver
    spec:
      nodeSelector:
        master: "true"
      hostNetwork: true
      containers:
      - name: kube-apiserver
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - /hyperkube
        - apiserver
        - --bind-address=0.0.0.0
        - --secure-port=443
        - --insecure-port=8080
        - --advertise-address=$(MY_POD_IP)
        - --etcd-servers={{ range $i, $e := .EtcdServers }}{{ if $i }},{{end}}{{ $e }}{{end}}
        - --storage-backend={{.StorageBackend}}
        - --allow-privileged=true
        - --service-cluster-ip-range=10.3.0.0/24
        - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota
        - --runtime-config=api/all=true
        - --tls-cert-file=/etc/kubernetes/secrets/apiserver.crt
        - --tls-private-key-file=/etc/kubernetes/secrets/apiserver.key
        - --service-account-key-file=/etc/kubernetes/secrets/service-account.pub
        - --client-ca-file=/etc/kubernetes/secrets/ca.crt
        - --cloud-provider={{ .CloudProvider  }}
        env:
          - name: MY_POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
        - mountPath: /etc/kubernetes/secrets
          name: secrets
          readOnly: true
      volumes:
      - name: ssl-certs-host
        hostPath:
          path: /usr/share/ca-certificates
      - name: secrets
        secret:
          secretName: kube-apiserver
`)
	CheckpointerTemplate = []byte(`apiVersion: "extensions/v1beta1"
kind: DaemonSet
metadata:
  name: checkpoint-installer
  namespace: kube-system
  labels:
    k8s-app: kube-api-checkpointer
spec:
  template:
    metadata:
      labels:
        k8s-app: kube-api-checkpointer
    spec:
      nodeSelector:
        master: "true"
      hostNetwork: true
      containers:
      - name: checkpoint-installer
        image: quay.io/coreos/pod-checkpointer:808d9e50c8beaa82672cb7dbd57a782d7d6c0262
        command:
        - /checkpoint-installer.sh
        volumeMounts:
        - mountPath: /etc/kubernetes/manifests
          name: etc-k8s-manifests
      volumes:
      - name: etc-k8s-manifests
        hostPath:
          path: /etc/kubernetes/manifests
`)
	ControllerManagerTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-controller-manager
  namespace: kube-system
  labels:
    k8s-app: kube-controller-manager
spec:
  replicas: 2
  template:
    metadata:
      labels:
        k8s-app: kube-controller-manager
    spec:
      containers:
      - name: kube-controller-manager
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - ./hyperkube
        - controller-manager
        - --root-ca-file=/etc/kubernetes/secrets/ca.crt
        - --service-account-private-key-file=/etc/kubernetes/secrets/service-account.key
        - --leader-elect=true
        - --cloud-provider={{ .CloudProvider  }}
        - --configure-cloud-routes=false
        volumeMounts:
        - name: secrets
          mountPath: /etc/kubernetes/secrets
          readOnly: true
        - name: ssl-host
          mountPath: /etc/ssl/certs
          readOnly: true
      volumes:
      - name: secrets
        secret:
          secretName: kube-controller-manager
      - name: ssl-host
        hostPath:
          path: /usr/share/ca-certificates
      dnsPolicy: Default # Don't use cluster DNS.
`)
	SchedulerTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-scheduler
  namespace: kube-system
  labels:
    k8s-app: kube-scheduler
spec:
  replicas: 2
  template:
    metadata:
      labels:
        k8s-app: kube-scheduler
    spec:
      containers:
      - name: kube-scheduler
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - ./hyperkube
        - scheduler
        - --leader-elect=true
`)
	ProxyTemplate = []byte(`apiVersion: "extensions/v1beta1"
kind: DaemonSet
metadata:
  name: kube-proxy
  namespace: kube-system
  labels:
    k8s_app: kube-proxy
spec:
  template:
    metadata:
      labels:
        k8s_app: kube-proxy
    spec:
      hostNetwork: true
      containers:
      - name: kube-proxy
        image: quay.io/coreos/hyperkube:v1.4.6_coreos.0
        command:
        - /hyperkube
        - proxy
        - --kubeconfig=/etc/kubernetes/kubeconfig
        - --proxy-mode=iptables
        - --hostname-override=$(POD_IP)
        - --cluster-cidr=10.2.0.0/16
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
        - name: etc-kubernetes
          mountPath: /etc/kubernetes
          readOnly: true
      volumes:
      - hostPath:
          path: /usr/share/ca-certificates
        name: ssl-certs-host
      - name: etc-kubernetes
        hostPath:
          path: /etc/kubernetes
`)
	DNSDeploymentTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: kube-dns
  template:
    metadata:
      labels:
        k8s-app: kube-dns
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ''
        scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
    spec:
      containers:
      - name: kubedns
        image: gcr.io/google_containers/kubedns-amd64:1.8
        resources:
          # TODO: Set memory limits when we've profiled the container for large
          # clusters, then set request = limit to keep this container in
          # guaranteed class. Currently, this container falls into the
          # "burstable" category so the kubelet doesn't backoff from restarting it.
          limits:
            memory: 170Mi
          requests:
            cpu: 100m
            memory: 70Mi
        livenessProbe:
          httpGet:
            path: /healthz-kubedns
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /readiness
            port: 8081
            scheme: HTTP
          # we poll on pod startup for the Kubernetes master service and
          # only setup the /readiness HTTP server once that's available.
          initialDelaySeconds: 3
          timeoutSeconds: 5
        args:
        # command = "/kube-dns"
        - --domain=cluster.local.
        - --dns-port=10053
        ports:
        - containerPort: 10053
          name: dns-local
          protocol: UDP
        - containerPort: 10053
          name: dns-tcp-local
          protocol: TCP
      - name: dnsmasq
        image: gcr.io/google_containers/kube-dnsmasq-amd64:1.4
        livenessProbe:
          httpGet:
            path: /healthz-dnsmasq
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        args:
        - --cache-size=1000
        - --no-resolv
        - --server=127.0.0.1#10053
        - --log-facility=-
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
      - name: healthz
        image: gcr.io/google_containers/exechealthz-amd64:1.2
        resources:
          limits:
            memory: 50Mi
          requests:
            cpu: 10m
            # Note that this container shouldn't really need 50Mi of memory. The
            # limits are set higher than expected pending investigation on #29688.
            # The extra memory was stolen from the kubedns container to keep the
            # net memory requested by the pod constant.
            memory: 50Mi
        args:
        - --cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 >/dev/null
        - --url=/healthz-dnsmasq
        - --cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1:10053 >/dev/null
        - --url=/healthz-kubedns
        - --port=8080
        - --quiet
        ports:
        - containerPort: 8080
          protocol: TCP
      dnsPolicy: Default  # Don't use cluster DNS.
`)
	DNSSvcTemplate = []byte(`apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "KubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP:  10.3.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP
`)
	EtcdOperatorTemplate = []byte(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: etcd-operator
  namespace: kube-system
  labels:
    k8s-app: etcd-operator
spec:
  replicas: 1
  template:
    metadata:
      labels:
        k8s-app: etcd-operator
    spec:
      containers:
      - name: etcd-operator
        image: quay.io/coreos/etcd-operator
        env:
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
`)
	EtcdSvcTemplate = []byte(`apiVersion: v1
kind: Service
metadata:
  name: etcd-service
  namespace: kube-system
spec:
  selector:
    app: etcd
    etcd_cluster: etcd-cluster
  clusterIP: 10.3.0.15
  ports:
  - name: client
    port: 2379
    protocol: TCP
`)
)
<|endoftext|>


<commit_before>package logging

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"github.com/sirupsen/logrus"
)

const thisModule = "github.com/telepresenceio/telepresence/v2"

// Formatter formats log messages for Telepresence client
type Formatter struct {
	timestampFormat string
}

func NewFormatter(timestampFormat string) *Formatter {
	return &Formatter{timestampFormat: timestampFormat}
}

// Format implements logrus.Formatter
func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
	var b *bytes.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &bytes.Buffer{}
	}
	data := make(logrus.Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}
	goroutine, _ := data["THREAD"].(string)
	delete(data, "THREAD")

	fmt.Fprintf(b, "%s %-*s %s : %s",
		entry.Time.Format(f.timestampFormat),
		len("warning"), entry.Level,
		strings.TrimPrefix(goroutine, "/"),
		entry.Message)

	if len(data) > 0 {
		b.WriteString(" :")
		keys := make([]string, 0, len(data))
		for key := range data {
			keys = append(keys, key)
		}
		sort.Strings(keys)
		for _, key := range keys {
			val := data[key]
			fmt.Fprintf(b, " %s=%+v", key, val)
		}
		b.WriteByte(')')
	}

	if entry.HasCaller() && strings.HasPrefix(entry.Caller.File, thisModule+"/") {
		fmt.Fprintf(b, " (from %s:%d)", strings.TrimPrefix(entry.Caller.File, thisModule+"/"), entry.Caller.Line)
	}

	b.WriteByte('\n')

	return b.Bytes(), nil
}
<commit_msg>logging: Adjust the log formatter to work better with dexec<commit_after>package logging

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"github.com/sirupsen/logrus"
)

const thisModule = "github.com/telepresenceio/telepresence/v2"

// Formatter formats log messages for Telepresence client
type Formatter struct {
	timestampFormat string
}

func NewFormatter(timestampFormat string) *Formatter {
	return &Formatter{timestampFormat: timestampFormat}
}

// Format implements logrus.Formatter
func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
	var b *bytes.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &bytes.Buffer{}
	}
	data := make(logrus.Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}
	goroutine, _ := data["THREAD"].(string)
	delete(data, "THREAD")

	fmt.Fprintf(b, "%s %-*s %s : %s",
		entry.Time.Format(f.timestampFormat),
		len("warning"), entry.Level,
		strings.TrimPrefix(goroutine, "/"),
		entry.Message)

	if len(data) > 0 {
		b.WriteString(" :")
		keys := make([]string, 0, len(data))
		for key := range data {
			keys = append(keys, key)
		}
		sort.Slice(keys, func(i, j int) bool {
			orders := map[string]int{
				"dexec.pid":    -4,
				"dexec.stream": -3,
				"dexec.data":   -2,
				"dexec.err":    -1,
			}
			iOrd := orders[keys[i]]
			jOrd := orders[keys[j]]
			if iOrd != jOrd {
				return iOrd < jOrd
			}
			return keys[i] < keys[j]
		})
		for _, key := range keys {
			val := fmt.Sprintf("%+v", data[key])
			fmt.Fprintf(b, " %s=%q", key, val)
		}
	}

	if entry.HasCaller() && strings.HasPrefix(entry.Caller.File, thisModule+"/") {
		fmt.Fprintf(b, " (from %s:%d)", strings.TrimPrefix(entry.Caller.File, thisModule+"/"), entry.Caller.Line)
	}

	b.WriteByte('\n')

	return b.Bytes(), nil
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package watchers

import (
	"sync"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/tools/cache"

	"github.com/cilium/cilium/pkg/k8s"
	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	"github.com/cilium/cilium/pkg/k8s/informer"
	"github.com/cilium/cilium/pkg/k8s/watchers/subscriber"
	"github.com/cilium/cilium/pkg/kvstore"
	"github.com/cilium/cilium/pkg/lock"
	nodeTypes "github.com/cilium/cilium/pkg/node/types"
)

// RegisterCiliumNodeSubscriber allows registration of subscriber.CiliumNode implementations.
// On CiliumNode events all registered subscriber.CiliumNode implementations will
// have their event handling methods called in order of registration.
func (k *K8sWatcher) RegisterCiliumNodeSubscriber(s subscriber.CiliumNode) {
	k.CiliumNodeChain.Register(s)
}

func (k *K8sWatcher) ciliumNodeInit(ciliumNPClient *k8s.K8sCiliumClient, asyncControllers *sync.WaitGroup) {
	// CiliumNode objects are used for node discovery until the key-value
	// store is connected
	var once sync.Once
	for {
		swgNodes := lock.NewStoppableWaitGroup()
		ciliumNodeStore, ciliumNodeInformer := informer.NewInformer(
			cache.NewListWatchFromClient(ciliumNPClient.CiliumV2().RESTClient(),
				cilium_v2.CNPluralName, v1.NamespaceAll, fields.Everything()),
			&cilium_v2.CiliumNode{},
			0,
			cache.ResourceEventHandlerFuncs{
				AddFunc: func(obj interface{}) {
					var valid, equal bool
					defer func() { k.K8sEventReceived(metricCiliumNode, metricCreate, valid, equal) }()
					if ciliumNode := k8s.ObjToCiliumNode(obj); ciliumNode != nil {
						valid = true
						n := nodeTypes.ParseCiliumNode(ciliumNode)
						errs := k.CiliumNodeChain.OnAddCiliumNode(ciliumNode, swgNodes)
						if n.IsLocal() {
							return
						}
						k.nodeDiscoverManager.NodeUpdated(n)
						k.K8sEventProcessed(metricCiliumNode, metricCreate, errs == nil)
					}
				},
				UpdateFunc: func(oldObj, newObj interface{}) {
					var valid, equal bool
					defer func() { k.K8sEventReceived(metricCiliumNode, metricUpdate, valid, equal) }()
					if oldCN := k8s.ObjToCiliumNode(oldObj); oldCN != nil {
						if ciliumNode := k8s.ObjToCiliumNode(newObj); ciliumNode != nil {
							valid = true
							isLocal := k8s.IsLocalCiliumNode(ciliumNode)
							if oldCN.DeepEqual(ciliumNode) {
								equal = true
								if !isLocal {
									// For remote nodes, we return early here to avoid unnecessary update events if
									// nothing in the spec or status has changed. But for local nodes, we want to
									// propagate the new resource version (not compared in DeepEqual) such that any
									// CiliumNodeChain subscribers are able to perform updates to the local CiliumNode
									// object using the most recent resource version.
									return
								}
							}
							n := nodeTypes.ParseCiliumNode(ciliumNode)
							errs := k.CiliumNodeChain.OnUpdateCiliumNode(oldCN, ciliumNode, swgNodes)
							if isLocal {
								return
							}
							k.nodeDiscoverManager.NodeUpdated(n)
							k.K8sEventProcessed(metricCiliumNode, metricUpdate, errs == nil)
						}
					}
				},
				DeleteFunc: func(obj interface{}) {
					var valid, equal bool
					defer func() { k.K8sEventReceived(metricCiliumNode, metricDelete, valid, equal) }()
					ciliumNode := k8s.ObjToCiliumNode(obj)
					if ciliumNode == nil {
						return
					}
					valid = true
					n := nodeTypes.ParseCiliumNode(ciliumNode)
					errs := k.CiliumNodeChain.OnDeleteCiliumNode(ciliumNode, swgNodes)
					if errs != nil {
						valid = false
					}
					k.nodeDiscoverManager.NodeDeleted(n)
				},
			},
			k8s.ConvertToCiliumNode,
		)
		isConnected := make(chan struct{})
		// once isConnected is closed, it will stop waiting on caches to be
		// synchronized.
		k.blockWaitGroupToSyncResources(isConnected, swgNodes, ciliumNodeInformer.HasSynced, k8sAPIGroupCiliumNodeV2)

		k.ciliumNodeStoreMU.Lock()
		k.ciliumNodeStore = ciliumNodeStore
		k.ciliumNodeStoreMU.Unlock()

		once.Do(func() {
			// Signalize that we have put node controller in the wait group
			// to sync resources.
			asyncControllers.Done()
		})
		k.k8sAPIGroups.AddAPI(k8sAPIGroupCiliumNodeV2)
		go ciliumNodeInformer.Run(isConnected)

		<-kvstore.Connected()
		close(isConnected)

		log.Info("Connected to key-value store, stopping CiliumNode watcher")

		k.cancelWaitGroupToSyncResources(k8sAPIGroupCiliumNodeV2)
		k.k8sAPIGroups.RemoveAPI(k8sAPIGroupCiliumNodeV2)
		// Create a new node controller when we are disconnected with the
		// kvstore
		<-kvstore.Client().Disconnected()

		log.Info("Disconnected from key-value store, restarting CiliumNode watcher")
	}
}
<commit_msg>watchers: don't ignore CiliumNode events if labels get updated<commit_after>// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package watchers

import (
	"sync"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/tools/cache"

	"github.com/cilium/cilium/pkg/comparator"
	"github.com/cilium/cilium/pkg/k8s"
	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	"github.com/cilium/cilium/pkg/k8s/informer"
	"github.com/cilium/cilium/pkg/k8s/watchers/subscriber"
	"github.com/cilium/cilium/pkg/kvstore"
	"github.com/cilium/cilium/pkg/lock"
	nodeTypes "github.com/cilium/cilium/pkg/node/types"
)

// RegisterCiliumNodeSubscriber allows registration of subscriber.CiliumNode implementations.
// On CiliumNode events all registered subscriber.CiliumNode implementations will
// have their event handling methods called in order of registration.
func (k *K8sWatcher) RegisterCiliumNodeSubscriber(s subscriber.CiliumNode) {
	k.CiliumNodeChain.Register(s)
}

func (k *K8sWatcher) ciliumNodeInit(ciliumNPClient *k8s.K8sCiliumClient, asyncControllers *sync.WaitGroup) {
	// CiliumNode objects are used for node discovery until the key-value
	// store is connected
	var once sync.Once
	for {
		swgNodes := lock.NewStoppableWaitGroup()
		ciliumNodeStore, ciliumNodeInformer := informer.NewInformer(
			cache.NewListWatchFromClient(ciliumNPClient.CiliumV2().RESTClient(),
				cilium_v2.CNPluralName, v1.NamespaceAll, fields.Everything()),
			&cilium_v2.CiliumNode{},
			0,
			cache.ResourceEventHandlerFuncs{
				AddFunc: func(obj interface{}) {
					var valid, equal bool
					defer func() { k.K8sEventReceived(metricCiliumNode, metricCreate, valid, equal) }()
					if ciliumNode := k8s.ObjToCiliumNode(obj); ciliumNode != nil {
						valid = true
						n := nodeTypes.ParseCiliumNode(ciliumNode)
						errs := k.CiliumNodeChain.OnAddCiliumNode(ciliumNode, swgNodes)
						if n.IsLocal() {
							return
						}
						k.nodeDiscoverManager.NodeUpdated(n)
						k.K8sEventProcessed(metricCiliumNode, metricCreate, errs == nil)
					}
				},
				UpdateFunc: func(oldObj, newObj interface{}) {
					var valid, equal bool
					defer func() { k.K8sEventReceived(metricCiliumNode, metricUpdate, valid, equal) }()
					if oldCN := k8s.ObjToCiliumNode(oldObj); oldCN != nil {
						if ciliumNode := k8s.ObjToCiliumNode(newObj); ciliumNode != nil {
							valid = true
							isLocal := k8s.IsLocalCiliumNode(ciliumNode)
							if oldCN.DeepEqual(ciliumNode) &&
								comparator.MapStringEquals(oldCN.ObjectMeta.Labels, ciliumNode.ObjectMeta.Labels) {
								equal = true
								if !isLocal {
									// For remote nodes, we return early here to avoid unnecessary update events if
									// nothing in the spec or status has changed. But for local nodes, we want to
									// propagate the new resource version (not compared in DeepEqual) such that any
									// CiliumNodeChain subscribers are able to perform updates to the local CiliumNode
									// object using the most recent resource version.
									return
								}
							}
							n := nodeTypes.ParseCiliumNode(ciliumNode)
							errs := k.CiliumNodeChain.OnUpdateCiliumNode(oldCN, ciliumNode, swgNodes)
							if isLocal {
								return
							}
							k.nodeDiscoverManager.NodeUpdated(n)
							k.K8sEventProcessed(metricCiliumNode, metricUpdate, errs == nil)
						}
					}
				},
				DeleteFunc: func(obj interface{}) {
					var valid, equal bool
					defer func() { k.K8sEventReceived(metricCiliumNode, metricDelete, valid, equal) }()
					ciliumNode := k8s.ObjToCiliumNode(obj)
					if ciliumNode == nil {
						return
					}
					valid = true
					n := nodeTypes.ParseCiliumNode(ciliumNode)
					errs := k.CiliumNodeChain.OnDeleteCiliumNode(ciliumNode, swgNodes)
					if errs != nil {
						valid = false
					}
					k.nodeDiscoverManager.NodeDeleted(n)
				},
			},
			k8s.ConvertToCiliumNode,
		)
		isConnected := make(chan struct{})
		// once isConnected is closed, it will stop waiting on caches to be
		// synchronized.
		k.blockWaitGroupToSyncResources(isConnected, swgNodes, ciliumNodeInformer.HasSynced, k8sAPIGroupCiliumNodeV2)

		k.ciliumNodeStoreMU.Lock()
		k.ciliumNodeStore = ciliumNodeStore
		k.ciliumNodeStoreMU.Unlock()

		once.Do(func() {
			// Signalize that we have put node controller in the wait group
			// to sync resources.
			asyncControllers.Done()
		})
		k.k8sAPIGroups.AddAPI(k8sAPIGroupCiliumNodeV2)
		go ciliumNodeInformer.Run(isConnected)

		<-kvstore.Connected()
		close(isConnected)

		log.Info("Connected to key-value store, stopping CiliumNode watcher")

		k.cancelWaitGroupToSyncResources(k8sAPIGroupCiliumNodeV2)
		k.k8sAPIGroups.RemoveAPI(k8sAPIGroupCiliumNodeV2)
		// Create a new node controller when we are disconnected with the
		// kvstore
		<-kvstore.Client().Disconnected()

		log.Info("Disconnected from key-value store, restarting CiliumNode watcher")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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 cruntime

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/golang/glog"
)

// Docker contains Docker runtime state
type Docker struct {
	Socket string
	Runner CommandRunner
}

// Name is a human readable name for Docker
func (r *Docker) Name() string {
	return "Docker"
}

// Version retrieves the current version of this runtime
func (r *Docker) Version() (string, error) {
	// Note: the server daemon has to be running, for this call to return successfully
	ver, err := r.Runner.CombinedOutput("docker version --format '{{.Server.Version}}'")
	if err != nil {
		return "", err
	}

	return strings.Split(ver, "\n")[0], nil
}

// SocketPath returns the path to the socket file for Docker
func (r *Docker) SocketPath() string {
	return r.Socket
}

// DefaultCNI returns whether to use CNI networking by default
func (r *Docker) DefaultCNI() bool {
	return false
}

// Available returns an error if it is not possible to use this runtime on a host
func (r *Docker) Available() error {
	_, err := exec.LookPath("docker")
	return err
}

// Active returns if docker is active on the host
func (r *Docker) Active() bool {
	err := r.Runner.Run("systemctl is-active --quiet service docker")
	return err == nil
}

// Enable idempotently enables Docker on a host
func (r *Docker) Enable() error {
	if err := disableOthers(r, r.Runner); err != nil {
		glog.Warningf("disableOthers: %v", err)
	}
	return r.Runner.Run("sudo systemctl restart docker")
}

// Disable idempotently disables Docker on a host
func (r *Docker) Disable() error {
	return r.Runner.Run("sudo systemctl stop docker docker.socket")
}

// LoadImage loads an image into this runtime
func (r *Docker) LoadImage(path string) error {
	glog.Infof("Loading image: %s", path)
	return r.Runner.Run(fmt.Sprintf("docker load -i %s", path))
}

// KubeletOptions returns kubelet options for a runtime.
func (r *Docker) KubeletOptions() map[string]string {
	return map[string]string{
		"container-runtime": "docker",
	}
}

// ListContainers returns a list of containers
func (r *Docker) ListContainers(filter string) ([]string, error) {
	content, err := r.Runner.CombinedOutput(fmt.Sprintf(`docker ps -a --filter="name=%s" --format="{{.ID}}"`, filter))
	if err != nil {
		return nil, err
	}
	var ids []string
	for _, line := range strings.Split(content, "\n") {
		if line != "" {
			ids = append(ids, line)
		}
	}
	return ids, nil
}

// KillContainers forcibly removes a running container based on ID
func (r *Docker) KillContainers(ids []string) error {
	if len(ids) == 0 {
		return nil
	}
	glog.Infof("Killing containers: %s", ids)
	return r.Runner.Run(fmt.Sprintf("docker rm -f %s", strings.Join(ids, " ")))
}

// StopContainers stops a running container based on ID
func (r *Docker) StopContainers(ids []string) error {
	if len(ids) == 0 {
		return nil
	}
	glog.Infof("Stopping containers: %s", ids)
	return r.Runner.Run(fmt.Sprintf("docker stop %s", strings.Join(ids, " ")))
}

// ContainerLogCmd returns the command to retrieve the log for a container based on ID
func (r *Docker) ContainerLogCmd(id string, len int, follow bool) string {
	var cmd strings.Builder
	cmd.WriteString("docker logs ")
	if len > 0 {
		cmd.WriteString(fmt.Sprintf("--tail %d ", len))
	}
	if follow {
		cmd.WriteString("--follow ")
	}

	cmd.WriteString(id)
	return cmd.String()
}
<commit_msg>we should sudo systemctl start docker to start docker instead of restart it<commit_after>/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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 cruntime

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/golang/glog"
)

// Docker contains Docker runtime state
type Docker struct {
	Socket string
	Runner CommandRunner
}

// Name is a human readable name for Docker
func (r *Docker) Name() string {
	return "Docker"
}

// Version retrieves the current version of this runtime
func (r *Docker) Version() (string, error) {
	// Note: the server daemon has to be running, for this call to return successfully
	ver, err := r.Runner.CombinedOutput("docker version --format '{{.Server.Version}}'")
	if err != nil {
		return "", err
	}

	return strings.Split(ver, "\n")[0], nil
}

// SocketPath returns the path to the socket file for Docker
func (r *Docker) SocketPath() string {
	return r.Socket
}

// DefaultCNI returns whether to use CNI networking by default
func (r *Docker) DefaultCNI() bool {
	return false
}

// Available returns an error if it is not possible to use this runtime on a host
func (r *Docker) Available() error {
	_, err := exec.LookPath("docker")
	return err
}

// Active returns if docker is active on the host
func (r *Docker) Active() bool {
	err := r.Runner.Run("systemctl is-active --quiet service docker")
	return err == nil
}

// Enable idempotently enables Docker on a host
func (r *Docker) Enable() error {
	if err := disableOthers(r, r.Runner); err != nil {
		glog.Warningf("disableOthers: %v", err)
	}
	return r.Runner.Run("sudo systemctl start docker")
}

// Disable idempotently disables Docker on a host
func (r *Docker) Disable() error {
	return r.Runner.Run("sudo systemctl stop docker docker.socket")
}

// LoadImage loads an image into this runtime
func (r *Docker) LoadImage(path string) error {
	glog.Infof("Loading image: %s", path)
	return r.Runner.Run(fmt.Sprintf("docker load -i %s", path))
}

// KubeletOptions returns kubelet options for a runtime.
func (r *Docker) KubeletOptions() map[string]string {
	return map[string]string{
		"container-runtime": "docker",
	}
}

// ListContainers returns a list of containers
func (r *Docker) ListContainers(filter string) ([]string, error) {
	content, err := r.Runner.CombinedOutput(fmt.Sprintf(`docker ps -a --filter="name=%s" --format="{{.ID}}"`, filter))
	if err != nil {
		return nil, err
	}
	var ids []string
	for _, line := range strings.Split(content, "\n") {
		if line != "" {
			ids = append(ids, line)
		}
	}
	return ids, nil
}

// KillContainers forcibly removes a running container based on ID
func (r *Docker) KillContainers(ids []string) error {
	if len(ids) == 0 {
		return nil
	}
	glog.Infof("Killing containers: %s", ids)
	return r.Runner.Run(fmt.Sprintf("docker rm -f %s", strings.Join(ids, " ")))
}

// StopContainers stops a running container based on ID
func (r *Docker) StopContainers(ids []string) error {
	if len(ids) == 0 {
		return nil
	}
	glog.Infof("Stopping containers: %s", ids)
	return r.Runner.Run(fmt.Sprintf("docker stop %s", strings.Join(ids, " ")))
}

// ContainerLogCmd returns the command to retrieve the log for a container based on ID
func (r *Docker) ContainerLogCmd(id string, len int, follow bool) string {
	var cmd strings.Builder
	cmd.WriteString("docker logs ")
	if len > 0 {
		cmd.WriteString(fmt.Sprintf("--tail %d ", len))
	}
	if follow {
		cmd.WriteString("--follow ")
	}

	cmd.WriteString(id)
	return cmd.String()
}
<|endoftext|>