<commit_before>package service

import (
	"encoding/json"
	"fmt"

	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/suit"
)

type configService struct {
}

func (c *configService) error(message string) (*suit.ConfigurationScreen, error) {

	return &suit.ConfigurationScreen{
		Sections: []suit.Section{
			suit.Section{
				Contents: []suit.Typed{
					suit.Alert{
						Title:        "Error",
						Subtitle:     message,
						DisplayClass: "danger",
					},
				},
			},
		},
		Actions: []suit.Typed{
			suit.ReplyAction{
				Label: "Cancel",
				Name:  "list",
			},
		},
	}, nil
}
func (c *configService) list() (*suit.ConfigurationScreen, error) {

	var lights []suit.ActionListOption

	for _, light := range lightsConfig {
		lights = append(lights, suit.ActionListOption{
			Title:    light.Name,
			Subtitle: fmt.Sprintf("%d sensors, %d lights", len(light.Sensors), len(light.Lights)),
			Value:    light.ID,
		})
	}

	screen := suit.ConfigurationScreen{
		Title: "Security Lights",
		Sections: []suit.Section{
			suit.Section{
				Contents: []suit.Typed{
					suit.ActionList{
						Name:    "light",
						Options: lights,
						PrimaryAction: &suit.ReplyAction{
							Name:        "edit",
							DisplayIcon: "pencil",
						},
						SecondaryAction: &suit.ReplyAction{
							Name:         "delete",
							Label:        "Delete",
							DisplayIcon:  "trash",
							DisplayClass: "danger",
						},
					},
				},
			},
		},
		Actions: []suit.Typed{
			suit.CloseAction{
				Label: "Close",
			},
			suit.ReplyAction{
				Label:        "New Security Light",
				Name:         "new",
				DisplayClass: "success",
				DisplayIcon:  "star",
			},
		},
	}

	return &screen, nil
}

func (c *configService) Configure(request *model.ConfigurationRequest) (*suit.ConfigurationScreen, error) {
	log.Infof("Incoming configuration request. Action:%s Data:%s", request.Action, string(request.Data))

	switch request.Action {
	case "list":
		fallthrough
	case "":
		return c.list()
	case "new":
		return c.edit(SecurityLightConfig{
			Timeout: 5,
			Time: suit.TimeRange{
				From: "sunset",
				To:   "sunrise",
			},
		})
	case "edit":

		var vals map[string]string
		json.Unmarshal(request.Data, &vals)
		config, ok := lightsConfig[vals["light"]]

		if !ok {
			return c.error(fmt.Sprintf("Could not find light with id: %s", vals["light"]))
		}

		return c.edit(config)
	case "delete":

		var vals map[string]string
		json.Unmarshal(request.Data, &vals)
		deleteSecurityLight(vals["light"])

		return c.list()
	case "save":
		var lightConfig SecurityLightConfig
		err := json.Unmarshal(request.Data, &lightConfig)
		if err != nil {
			return nil, fmt.Errorf("Failed to unmarshal save config request %s: %s", request.Data, err)
		}

		saveSecurityLight(&lightConfig)
		return c.list()
	default:
		return c.error(fmt.Sprintf("Unknown action: %s", request.Action))
	}
}

func (c *configService) edit(config SecurityLightConfig) (*suit.ConfigurationScreen, error) {

	var sensorOptions []suit.OptionGroupOption
	sensors, err := getSensors()
	if err != nil {
		return c.error(fmt.Sprintf("Could not find sensors: %s", err))
	}

	for _, s := range sensors {
		sensorOptions = append(sensorOptions, suit.OptionGroupOption{
			Title:    s.Name,
			Value:    s.ID,
			Selected: contains(config.Sensors, s.ID),
		})
	}

	var lightOptions []suit.OptionGroupOption
	lights, err := getLights()
	if err != nil {
		return c.error(fmt.Sprintf("Could not find lights: %s", err))
	}

	for _, s := range lights {
		lightOptions = append(lightOptions, suit.OptionGroupOption{
			Title:    s.Name,
			Value:    s.ID,
			Selected: contains(config.Lights, s.ID),
		})
	}

	title := "New Security Light"
	if config.ID != "" {
		title = "Edit Security Light"
	}

	screen := suit.ConfigurationScreen{
		Title: title,
		Sections: []suit.Section{
			suit.Section{
				Contents: []suit.Typed{
					suit.InputHidden{
						Name:  "id",
						Value: config.ID,
					},
					suit.InputText{
						Name:        "name",
						Before:      "Name",
						Placeholder: "My Security Light",
						Value:       config.Name,
					},
					suit.Separator{},
					suit.OptionGroup{
						Name:           "sensors",
						Title:          "When these devices detect motion",
						MinimumChoices: 1,
						Options:        sensorOptions,
					},
					suit.OptionGroup{
						Name:           "lights",
						Title:          "Turn on these lights",
						MinimumChoices: 1,
						Options:        lightOptions,
					},
					suit.Separator{},
					suit.InputTimeRange{
						Name:  "time",
						Title: "When",
						Value: suit.TimeRange{
							From: config.Time.From,
							To:   config.Time.To,
						},
					},
					suit.InputText{
						Title:     "Turn off again after",
						After:     "minutes",
						Name:      "timeout",
						InputType: "number",
						Minimum:   i(0),
						Value:     config.Timeout,
					},
				},
			},
		},
		Actions: []suit.Typed{
			suit.CloseAction{
				Label: "Cancel",
			},
			suit.ReplyAction{
				Label:        "Save",
				Name:         "save",
				DisplayClass: "success",
				DisplayIcon:  "star",
			},
		},
	}

	return &screen, nil
}

func i(i int) *int {
	return &i
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}
<commit_msg>Moar icons<commit_after>package service

import (
	"encoding/json"
	"fmt"

	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/suit"
)

type configService struct {
}

func (c *configService) error(message string) (*suit.ConfigurationScreen, error) {

	return &suit.ConfigurationScreen{
		Sections: []suit.Section{
			suit.Section{
				Contents: []suit.Typed{
					suit.Alert{
						Title:        "Error",
						Subtitle:     message,
						DisplayClass: "danger",
					},
				},
			},
		},
		Actions: []suit.Typed{
			suit.ReplyAction{
				Label: "Cancel",
				Name:  "list",
			},
		},
	}, nil
}
func (c *configService) list() (*suit.ConfigurationScreen, error) {

	var lights []suit.ActionListOption

	for _, light := range lightsConfig {
		lights = append(lights, suit.ActionListOption{
			Title:    light.Name,
			Subtitle: fmt.Sprintf("%d sensors, %d lights", len(light.Sensors), len(light.Lights)),
			Value:    light.ID,
		})
	}

	screen := suit.ConfigurationScreen{
		Title:       "Security Lights",
		DisplayIcon: "lightbulb-o",
		Sections: []suit.Section{
			suit.Section{
				Contents: []suit.Typed{
					suit.ActionList{
						Name:    "light",
						Options: lights,
						PrimaryAction: &suit.ReplyAction{
							Name:        "edit",
							DisplayIcon: "pencil",
						},
						SecondaryAction: &suit.ReplyAction{
							Name:         "delete",
							Label:        "Delete",
							DisplayIcon:  "trash",
							DisplayClass: "danger",
						},
					},
				},
			},
		},
		Actions: []suit.Typed{
			suit.CloseAction{
				Label: "Close",
			},
			suit.ReplyAction{
				Label:        "New Security Light",
				Name:         "new",
				DisplayClass: "success",
				DisplayIcon:  "star",
			},
		},
	}

	return &screen, nil
}

func (c *configService) Configure(request *model.ConfigurationRequest) (*suit.ConfigurationScreen, error) {
	log.Infof("Incoming configuration request. Action:%s Data:%s", request.Action, string(request.Data))

	switch request.Action {
	case "list":
		fallthrough
	case "":
		return c.list()
	case "new":
		return c.edit(SecurityLightConfig{
			Timeout: 5,
			Time: suit.TimeRange{
				From: "sunset",
				To:   "sunrise",
			},
		})
	case "edit":

		var vals map[string]string
		json.Unmarshal(request.Data, &vals)
		config, ok := lightsConfig[vals["light"]]

		if !ok {
			return c.error(fmt.Sprintf("Could not find light with id: %s", vals["light"]))
		}

		return c.edit(config)
	case "delete":

		var vals map[string]string
		json.Unmarshal(request.Data, &vals)
		deleteSecurityLight(vals["light"])

		return c.list()
	case "save":
		var lightConfig SecurityLightConfig
		err := json.Unmarshal(request.Data, &lightConfig)
		if err != nil {
			return nil, fmt.Errorf("Failed to unmarshal save config request %s: %s", request.Data, err)
		}

		saveSecurityLight(&lightConfig)
		return c.list()
	default:
		return c.error(fmt.Sprintf("Unknown action: %s", request.Action))
	}
}

func (c *configService) edit(config SecurityLightConfig) (*suit.ConfigurationScreen, error) {

	var sensorOptions []suit.OptionGroupOption
	sensors, err := getSensors()
	if err != nil {
		return c.error(fmt.Sprintf("Could not find sensors: %s", err))
	}

	for _, s := range sensors {
		sensorOptions = append(sensorOptions, suit.OptionGroupOption{
			Title:    s.Name,
			Value:    s.ID,
			Selected: contains(config.Sensors, s.ID),
		})
	}

	var lightOptions []suit.OptionGroupOption
	lights, err := getLights()
	if err != nil {
		return c.error(fmt.Sprintf("Could not find lights: %s", err))
	}

	for _, s := range lights {
		lightOptions = append(lightOptions, suit.OptionGroupOption{
			Title:    s.Name,
			Value:    s.ID,
			Selected: contains(config.Lights, s.ID),
		})
	}

	title := "New Security Light"
	if config.ID != "" {
		title = "Edit Security Light"
	}

	screen := suit.ConfigurationScreen{
		Title:       title,
		DisplayIcon: "lightbulb-o",
		Sections: []suit.Section{
			suit.Section{
				Contents: []suit.Typed{
					suit.InputHidden{
						Name:  "id",
						Value: config.ID,
					},
					suit.InputText{
						Name:        "name",
						Before:      "Name",
						Placeholder: "My Security Light",
						Value:       config.Name,
					},
					suit.Separator{},
					suit.OptionGroup{
						Name:           "sensors",
						Title:          "When these devices detect motion",
						MinimumChoices: 1,
						Options:        sensorOptions,
					},
					suit.OptionGroup{
						Name:           "lights",
						Title:          "Turn on these lights",
						MinimumChoices: 1,
						Options:        lightOptions,
					},
					suit.Separator{},
					suit.InputTimeRange{
						Name:  "time",
						Title: "When",
						Value: suit.TimeRange{
							From: config.Time.From,
							To:   config.Time.To,
						},
					},
					suit.InputText{
						Title:     "Turn off again after",
						After:     "minutes",
						Name:      "timeout",
						InputType: "number",
						Minimum:   i(0),
						Value:     config.Timeout,
					},
				},
			},
		},
		Actions: []suit.Typed{
			suit.CloseAction{
				Label: "Cancel",
			},
			suit.ReplyAction{
				Label:        "Save",
				Name:         "save",
				DisplayClass: "success",
				DisplayIcon:  "star",
			},
		},
	}

	return &screen, nil
}

func i(i int) *int {
	return &i
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	logging "github.com/op/go-logging"
	"github.com/pkg/errors"
)

const (
	stopContainerTimeout = 60 // Seconds before a container is killed (after graceful stop)
)

// NewDockerRunner creates a runner that starts processes on the local OS.
func NewDockerRunner(log *logging.Logger, endpoint, image, user, volumesFrom string, gcDelay time.Duration, netHost, privileged bool) (Runner, error) {
	client, err := docker.NewClient(endpoint)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerRunner{
		log:          log,
		client:       client,
		image:        image,
		user:         user,
		volumesFrom:  volumesFrom,
		containerIDs: make(map[string]time.Time),
		gcDelay:      gcDelay,
		netHost:      netHost,
		privileged:   privileged,
	}, nil
}

// dockerRunner implements a Runner that starts processes in a docker container.
type dockerRunner struct {
	log          *logging.Logger
	client       *docker.Client
	image        string
	user         string
	volumesFrom  string
	mutex        sync.Mutex
	containerIDs map[string]time.Time
	gcOnce       sync.Once
	gcDelay      time.Duration
	netHost      bool
	privileged   bool
}

type dockerContainer struct {
	client    *docker.Client
	container *docker.Container
}

func (r *dockerRunner) GetContainerDir(hostDir string) string {
	if r.volumesFrom != "" {
		return hostDir
	}
	return "/data"
}

func (r *dockerRunner) Start(command string, args []string, volumes []Volume, ports []int, containerName string) (Process, error) {
	// Start gc (once)
	r.gcOnce.Do(func() { go r.gc() })

	// Pull docker image
	if err := r.pullImage(r.image); err != nil {
		return nil, maskAny(err)
	}

	// Ensure container name is valid
	containerName = strings.Replace(containerName, ":", "", -1)

	var result Process
	op := func() error {
		// Make sure the container is really gone
		r.log.Debugf("Removing container '%s' (if it exists)", containerName)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:    containerName,
			Force: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Errorf("Failed to remove container '%s': %v", containerName, err)
		}
		// Try starting it now
		p, err := r.start(command, args, volumes, ports, containerName)
		if err != nil {
			return maskAny(err)
		}
		result = p
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return nil, maskAny(err)
	}
	return result, nil
}

// Try to start a command with given arguments
func (r *dockerRunner) start(command string, args []string, volumes []Volume, ports []int, containerName string) (Process, error) {
	opts := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Image:        r.image,
			Entrypoint:   []string{command},
			Cmd:          args,
			Tty:          true,
			User:         r.user,
			ExposedPorts: make(map[docker.Port]struct{}),
		},
		HostConfig: &docker.HostConfig{
			PortBindings:    make(map[docker.Port][]docker.PortBinding),
			PublishAllPorts: false,
			AutoRemove:      false,
			Privileged:      r.privileged,
		},
	}
	if r.volumesFrom != "" {
		opts.HostConfig.VolumesFrom = []string{r.volumesFrom}
	} else {
		for _, v := range volumes {
			bind := fmt.Sprintf("%s:%s", v.HostPath, v.ContainerPath)
			if v.ReadOnly {
				bind = bind + ":ro"
			}
			opts.HostConfig.Binds = append(opts.HostConfig.Binds, bind)
		}
	}
	if r.netHost {
		opts.HostConfig.NetworkMode = "host"
	} else {
		for _, p := range ports {
			dockerPort := docker.Port(fmt.Sprintf("%d/tcp", p))
			opts.Config.ExposedPorts[dockerPort] = struct{}{}
			opts.HostConfig.PortBindings[dockerPort] = []docker.PortBinding{
				docker.PortBinding{
					HostIP:   "0.0.0.0",
					HostPort: strconv.Itoa(p),
				},
			}
		}
	}
	r.log.Debugf("Creating container %s", containerName)
	c, err := r.client.CreateContainer(opts)
	if err != nil {
		return nil, maskAny(err)
	}
	r.recordContainerID(c.ID) // Record ID so we can clean it up later
	r.log.Debugf("Starting container %s", containerName)
	if err := r.client.StartContainer(c.ID, opts.HostConfig); err != nil {
		return nil, maskAny(err)
	}
	r.log.Debugf("Started container %s", containerName)
	return &dockerContainer{
		client:    r.client,
		container: c,
	}, nil
}

// pullImage tries to pull the given image.
// It retries several times upon failure.
func (r *dockerRunner) pullImage(image string) error {
	// Pull docker image
	repo, tag := docker.ParseRepositoryTag(r.image)

	op := func() error {
		r.log.Debugf("Pulling image %s:%s", repo, tag)
		if err := r.client.PullImage(docker.PullImageOptions{
			Repository: repo,
			Tag:        tag,
		}, docker.AuthConfiguration{}); err != nil {
			if isNotFound(err) {
				return maskAny(&PermanentError{err})
			}
			return maskAny(err)
		}
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return maskAny(err)
	}
	return nil
}

func (r *dockerRunner) CreateStartArangodbCommand(index int, masterIP string, masterPort string) string {
	addr := masterIP
	hostPort := 4000 + (portOffsetIncrement * (index - 1))
	if masterPort != "" {
		addr = addr + ":" + masterPort
		masterPortI, _ := strconv.Atoi(masterPort)
		hostPort = masterPortI + (portOffsetIncrement * (index - 1))
	}
	lines := []string{
		fmt.Sprintf("docker volume create arangodb%d &&", index),
		fmt.Sprintf("docker run -it --name=adb%d --rm -p %d:4000 -v arangodb%d:/data -v /var/run/docker.sock:/var/run/docker.sock arangodb/arangodb-starter", index, hostPort, index),
		fmt.Sprintf("--dockerContainer=adb%d --ownAddress=%s --join=%s", index, masterIP, addr),
	}
	return strings.Join(lines, " \\\n    ")
}

// Cleanup after all processes are dead and have been cleaned themselves
func (r *dockerRunner) Cleanup() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	for id := range r.containerIDs {
		r.log.Infof("Removing container %s", id)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:            id,
			Force:         true,
			RemoveVolumes: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Warningf("Failed to remove container %s: %#v", id, err)
		}
	}
	r.containerIDs = nil

	return nil
}

// recordContainerID records an ID of a created container
func (r *dockerRunner) recordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.containerIDs[id] = time.Now()
}

// unrecordContainerID removes an ID from the list of created containers
func (r *dockerRunner) unrecordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	delete(r.containerIDs, id)
}

// gc performs continues garbage collection of stopped old containers
func (r *dockerRunner) gc() {
	canGC := func(c *docker.Container) bool {
		gcBoundary := time.Now().UTC().Add(-r.gcDelay)
		switch c.State.StateString() {
		case "dead", "exited":
			if c.State.FinishedAt.Before(gcBoundary) {
				// Dead or exited long enough
				return true
			}
		case "created":
			if c.Created.Before(gcBoundary) {
				// Created but not running long enough
				return true
			}
		}
		return false
	}
	for {
		ids := r.gatherCollectableContainerIDs()
		for _, id := range ids {
			c, err := r.client.InspectContainer(id)
			if err != nil {
				if isNoSuchContainer(err) {
					// container no longer exists
					r.unrecordContainerID(id)
				} else {
					r.log.Warningf("Failed to inspect container %s: %#v", id, err)
				}
			} else if canGC(c) {
				// Container is dead for more than 10 minutes, gc it.
				r.log.Infof("Removing old container %s", id)
				if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
					ID:            id,
					RemoveVolumes: true,
				}); err != nil {
					r.log.Warningf("Failed to remove container %s: %#v", id, err)
				} else {
					// Remove succeeded
					r.unrecordContainerID(id)
				}
			}
		}
		time.Sleep(time.Minute)
	}
}

// gatherCollectableContainerIDs returns all container ID's that are old enough to be consider for garbage collection.
func (r *dockerRunner) gatherCollectableContainerIDs() []string {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	var result []string
	gcBoundary := time.Now().Add(-r.gcDelay)
	for id, ts := range r.containerIDs {
		if ts.Before(gcBoundary) {
			result = append(result, id)
		}
	}
	return result
}

// ProcessID returns the pid of the process (if not running in docker)
func (p *dockerContainer) ProcessID() int {
	return 0
}

// ContainerID returns the ID of the docker container that runs the process.
func (p *dockerContainer) ContainerID() string {
	return p.container.ID
}

// ContainerIP returns the IP address of the docker container that runs the process.
func (p *dockerContainer) ContainerIP() string {
	if ns := p.container.NetworkSettings; ns != nil {
		return ns.IPAddress
	}
	return ""
}

func (p *dockerContainer) Wait() {
	p.client.WaitContainer(p.container.ID)
}

func (p *dockerContainer) Terminate() error {
	if err := p.client.StopContainer(p.container.ID, stopContainerTimeout); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Kill() error {
	if err := p.client.KillContainer(docker.KillContainerOptions{
		ID: p.container.ID,
	}); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Cleanup() error {
	opts := docker.RemoveContainerOptions{
		ID:            p.container.ID,
		Force:         true,
		RemoveVolumes: true,
	}
	if err := p.client.RemoveContainer(opts); err != nil {
		return maskAny(err)
	}
	return nil
}

// isNoSuchContainer returns true if the given error is (or is caused by) a NoSuchContainer error.
func isNoSuchContainer(err error) bool {
	if _, ok := err.(*docker.NoSuchContainer); ok {
		return true
	}
	if _, ok := errors.Cause(err).(*docker.NoSuchContainer); ok {
		return true
	}
	return false
}

// isNotFound returns true if the given error is (or is caused by) a 404 response error.
func isNotFound(err error) bool {
	if err, ok := errors.Cause(err).(*docker.Error); ok {
		return err.Status == 404
	}
	return false
}
<commit_msg>Additional inspect to fetch container data<commit_after>package service

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	logging "github.com/op/go-logging"
	"github.com/pkg/errors"
)

const (
	stopContainerTimeout = 60 // Seconds before a container is killed (after graceful stop)
)

// NewDockerRunner creates a runner that starts processes on the local OS.
func NewDockerRunner(log *logging.Logger, endpoint, image, user, volumesFrom string, gcDelay time.Duration, netHost, privileged bool) (Runner, error) {
	client, err := docker.NewClient(endpoint)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerRunner{
		log:          log,
		client:       client,
		image:        image,
		user:         user,
		volumesFrom:  volumesFrom,
		containerIDs: make(map[string]time.Time),
		gcDelay:      gcDelay,
		netHost:      netHost,
		privileged:   privileged,
	}, nil
}

// dockerRunner implements a Runner that starts processes in a docker container.
type dockerRunner struct {
	log          *logging.Logger
	client       *docker.Client
	image        string
	user         string
	volumesFrom  string
	mutex        sync.Mutex
	containerIDs map[string]time.Time
	gcOnce       sync.Once
	gcDelay      time.Duration
	netHost      bool
	privileged   bool
}

type dockerContainer struct {
	client    *docker.Client
	container *docker.Container
}

func (r *dockerRunner) GetContainerDir(hostDir string) string {
	if r.volumesFrom != "" {
		return hostDir
	}
	return "/data"
}

func (r *dockerRunner) Start(command string, args []string, volumes []Volume, ports []int, containerName string) (Process, error) {
	// Start gc (once)
	r.gcOnce.Do(func() { go r.gc() })

	// Pull docker image
	if err := r.pullImage(r.image); err != nil {
		return nil, maskAny(err)
	}

	// Ensure container name is valid
	containerName = strings.Replace(containerName, ":", "", -1)

	var result Process
	op := func() error {
		// Make sure the container is really gone
		r.log.Debugf("Removing container '%s' (if it exists)", containerName)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:    containerName,
			Force: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Errorf("Failed to remove container '%s': %v", containerName, err)
		}
		// Try starting it now
		p, err := r.start(command, args, volumes, ports, containerName)
		if err != nil {
			return maskAny(err)
		}
		result = p
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return nil, maskAny(err)
	}
	return result, nil
}

// Try to start a command with given arguments
func (r *dockerRunner) start(command string, args []string, volumes []Volume, ports []int, containerName string) (Process, error) {
	opts := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Image:        r.image,
			Entrypoint:   []string{command},
			Cmd:          args,
			Tty:          true,
			User:         r.user,
			ExposedPorts: make(map[docker.Port]struct{}),
		},
		HostConfig: &docker.HostConfig{
			PortBindings:    make(map[docker.Port][]docker.PortBinding),
			PublishAllPorts: false,
			AutoRemove:      false,
			Privileged:      r.privileged,
		},
	}
	if r.volumesFrom != "" {
		opts.HostConfig.VolumesFrom = []string{r.volumesFrom}
	} else {
		for _, v := range volumes {
			bind := fmt.Sprintf("%s:%s", v.HostPath, v.ContainerPath)
			if v.ReadOnly {
				bind = bind + ":ro"
			}
			opts.HostConfig.Binds = append(opts.HostConfig.Binds, bind)
		}
	}
	if r.netHost {
		opts.HostConfig.NetworkMode = "host"
	} else {
		for _, p := range ports {
			dockerPort := docker.Port(fmt.Sprintf("%d/tcp", p))
			opts.Config.ExposedPorts[dockerPort] = struct{}{}
			opts.HostConfig.PortBindings[dockerPort] = []docker.PortBinding{
				docker.PortBinding{
					HostIP:   "0.0.0.0",
					HostPort: strconv.Itoa(p),
				},
			}
		}
	}
	r.log.Debugf("Creating container %s", containerName)
	c, err := r.client.CreateContainer(opts)
	if err != nil {
		return nil, maskAny(err)
	}
	r.recordContainerID(c.ID) // Record ID so we can clean it up later
	r.log.Debugf("Starting container %s", containerName)
	if err := r.client.StartContainer(c.ID, opts.HostConfig); err != nil {
		return nil, maskAny(err)
	}
	r.log.Debugf("Started container %s", containerName)
	// Inspect container to make sure we have the latest info
	c, err = r.client.InspectContainer(c.ID)
	if err != nil {
		return nil, maskAny(err)
	}
	return &dockerContainer{
		client:    r.client,
		container: c,
	}, nil
}

// pullImage tries to pull the given image.
// It retries several times upon failure.
func (r *dockerRunner) pullImage(image string) error {
	// Pull docker image
	repo, tag := docker.ParseRepositoryTag(r.image)

	op := func() error {
		r.log.Debugf("Pulling image %s:%s", repo, tag)
		if err := r.client.PullImage(docker.PullImageOptions{
			Repository: repo,
			Tag:        tag,
		}, docker.AuthConfiguration{}); err != nil {
			if isNotFound(err) {
				return maskAny(&PermanentError{err})
			}
			return maskAny(err)
		}
		return nil
	}

	if err := retry(op, time.Minute*2); err != nil {
		return maskAny(err)
	}
	return nil
}

func (r *dockerRunner) CreateStartArangodbCommand(index int, masterIP string, masterPort string) string {
	addr := masterIP
	hostPort := 4000 + (portOffsetIncrement * (index - 1))
	if masterPort != "" {
		addr = addr + ":" + masterPort
		masterPortI, _ := strconv.Atoi(masterPort)
		hostPort = masterPortI + (portOffsetIncrement * (index - 1))
	}
	lines := []string{
		fmt.Sprintf("docker volume create arangodb%d &&", index),
		fmt.Sprintf("docker run -it --name=adb%d --rm -p %d:4000 -v arangodb%d:/data -v /var/run/docker.sock:/var/run/docker.sock arangodb/arangodb-starter", index, hostPort, index),
		fmt.Sprintf("--dockerContainer=adb%d --ownAddress=%s --join=%s", index, masterIP, addr),
	}
	return strings.Join(lines, " \\\n    ")
}

// Cleanup after all processes are dead and have been cleaned themselves
func (r *dockerRunner) Cleanup() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	for id := range r.containerIDs {
		r.log.Infof("Removing container %s", id)
		if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
			ID:            id,
			Force:         true,
			RemoveVolumes: true,
		}); err != nil && !isNoSuchContainer(err) {
			r.log.Warningf("Failed to remove container %s: %#v", id, err)
		}
	}
	r.containerIDs = nil

	return nil
}

// recordContainerID records an ID of a created container
func (r *dockerRunner) recordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.containerIDs[id] = time.Now()
}

// unrecordContainerID removes an ID from the list of created containers
func (r *dockerRunner) unrecordContainerID(id string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	delete(r.containerIDs, id)
}

// gc performs continues garbage collection of stopped old containers
func (r *dockerRunner) gc() {
	canGC := func(c *docker.Container) bool {
		gcBoundary := time.Now().UTC().Add(-r.gcDelay)
		switch c.State.StateString() {
		case "dead", "exited":
			if c.State.FinishedAt.Before(gcBoundary) {
				// Dead or exited long enough
				return true
			}
		case "created":
			if c.Created.Before(gcBoundary) {
				// Created but not running long enough
				return true
			}
		}
		return false
	}
	for {
		ids := r.gatherCollectableContainerIDs()
		for _, id := range ids {
			c, err := r.client.InspectContainer(id)
			if err != nil {
				if isNoSuchContainer(err) {
					// container no longer exists
					r.unrecordContainerID(id)
				} else {
					r.log.Warningf("Failed to inspect container %s: %#v", id, err)
				}
			} else if canGC(c) {
				// Container is dead for more than 10 minutes, gc it.
				r.log.Infof("Removing old container %s", id)
				if err := r.client.RemoveContainer(docker.RemoveContainerOptions{
					ID:            id,
					RemoveVolumes: true,
				}); err != nil {
					r.log.Warningf("Failed to remove container %s: %#v", id, err)
				} else {
					// Remove succeeded
					r.unrecordContainerID(id)
				}
			}
		}
		time.Sleep(time.Minute)
	}
}

// gatherCollectableContainerIDs returns all container ID's that are old enough to be consider for garbage collection.
func (r *dockerRunner) gatherCollectableContainerIDs() []string {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	var result []string
	gcBoundary := time.Now().Add(-r.gcDelay)
	for id, ts := range r.containerIDs {
		if ts.Before(gcBoundary) {
			result = append(result, id)
		}
	}
	return result
}

// ProcessID returns the pid of the process (if not running in docker)
func (p *dockerContainer) ProcessID() int {
	return 0
}

// ContainerID returns the ID of the docker container that runs the process.
func (p *dockerContainer) ContainerID() string {
	return p.container.ID
}

// ContainerIP returns the IP address of the docker container that runs the process.
func (p *dockerContainer) ContainerIP() string {
	if ns := p.container.NetworkSettings; ns != nil {
		return ns.IPAddress
	}
	return ""
}

func (p *dockerContainer) Wait() {
	p.client.WaitContainer(p.container.ID)
}

func (p *dockerContainer) Terminate() error {
	if err := p.client.StopContainer(p.container.ID, stopContainerTimeout); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Kill() error {
	if err := p.client.KillContainer(docker.KillContainerOptions{
		ID: p.container.ID,
	}); err != nil {
		return maskAny(err)
	}
	return nil
}

func (p *dockerContainer) Cleanup() error {
	opts := docker.RemoveContainerOptions{
		ID:            p.container.ID,
		Force:         true,
		RemoveVolumes: true,
	}
	if err := p.client.RemoveContainer(opts); err != nil {
		return maskAny(err)
	}
	return nil
}

// isNoSuchContainer returns true if the given error is (or is caused by) a NoSuchContainer error.
func isNoSuchContainer(err error) bool {
	if _, ok := err.(*docker.NoSuchContainer); ok {
		return true
	}
	if _, ok := errors.Cause(err).(*docker.NoSuchContainer); ok {
		return true
	}
	return false
}

// isNotFound returns true if the given error is (or is caused by) a 404 response error.
func isNotFound(err error) bool {
	if err, ok := errors.Cause(err).(*docker.Error); ok {
		return err.Status == 404
	}
	return false
}
<|endoftext|>


<commit_before>package sms

import (
	"testing"

	"encoding/json"

	"github.com/smancke/guble/server/kvstore"
	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"

	"strings"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server/store"
	"github.com/smancke/guble/server/store/dummystore"
)

func Test_StartStop(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)

	msgStore := dummystore.New(kvStore)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)
	topic := "sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	err = gw.Start()
	a.NoError(err)

	err = gw.Stop()
	a.NoError(err)

	time.Sleep(100 * time.Millisecond)
}

func Test_SendOneSms(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()

	defer testutil.EnableDebugForMethod()()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)
	msgStore := dummystore.New(kvStore)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)

	topic := "/sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	err = gw.Start()
	a.NoError(err)

	sms := NexmoSms{
		To:   "toNumber",
		From: "FromNUmber",
		Text: "body",
	}
	d, err := json.Marshal(&sms)
	a.NoError(err)

	msg := protocol.Message{
		Path: protocol.Path(topic),
		ID:   uint64(4),
		Body: d,
	}

	mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Return(nil)
	a.NotNil(gw.route)
	gw.route.Deliver(&msg)
	time.Sleep(100 * time.Millisecond)

	err = gw.Stop()
	a.NoError(err)

	err = gw.ReadLastID()
	a.NoError(err)

	time.Sleep(100 * time.Millisecond)
}

func Test_Restart(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()
	defer testutil.EnableDebugForMethod()()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)
	msgStore := NewMockMessageStore(ctrl)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)

	topic := "/sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	err = gw.Start()
	a.NoError(err)

	sms := NexmoSms{
		To:   "toNumber",
		From: "FromNUmber",
		Text: "body",
	}
	d, err := json.Marshal(&sms)
	a.NoError(err)

	msg := protocol.Message{
		Path:          protocol.Path(topic),
		UserID:        "samsa",
		ApplicationID: "sms",
		ID:            uint64(4),
		Body:          d,
	}

	msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(0), nil)
	msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(4), nil)
	msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(4), nil)
	mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Times(1).Return(ErrNoSMSSent)

	routerMock.EXPECT().Fetch(gomock.Any()).Do(func(r *store.FetchRequest) {
		go func() {

			logger.WithField("r.Partition", r.Partition).Info("----")

			a.Equal(strings.Split(topic, "/")[1], r.Partition)

			r.StartC <- 1

			r.MessageC <- &store.FetchedMessage{ID: uint64(4), Message: msg.Bytes()}
			close(r.MessageC)
		}()
	})
	doneC := make(chan bool)
	routerMock.EXPECT().Done().AnyTimes().Return(doneC)

	mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Return(nil)

	a.NotNil(gw.route)
	gw.route.Deliver(&msg)
	time.Sleep(100 * time.Millisecond)
}

func TestReadLastID(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()

	defer testutil.EnableDebugForMethod()()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)
	msgStore := dummystore.New(kvStore)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)

	topic := "/sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	gw.SetLastSentID(uint64(10))

	gw.ReadLastID()

	a.Equal(uint64(10), gw.LastIDSent)
}
<commit_msg>small refactoring<commit_after>package sms

import (
	"testing"

	"encoding/json"

	"github.com/smancke/guble/server/kvstore"
	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"

	"strings"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server/router"
	"github.com/smancke/guble/server/store/dummystore"
)

func Test_StartStop(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)

	msgStore := dummystore.New(kvStore)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)
	topic := "sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) (*router.Route, error) {
		a.Equal(topic, r.Path.Partition())
		return r, nil
	})

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	err = gw.Start()
	a.NoError(err)

	err = gw.Stop()
	a.NoError(err)

	time.Sleep(100 * time.Millisecond)
}

func Test_SendOneSms(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()

	defer testutil.EnableDebugForMethod()()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)
	msgStore := dummystore.New(kvStore)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)

	topic := "/sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) (*router.Route, error) {
		a.Equal(topic, r.Path.Partition())
		return r, nil
	})

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	err = gw.Start()
	a.NoError(err)

	sms := NexmoSms{
		To:   "toNumber",
		From: "FromNUmber",
		Text: "body",
	}
	d, err := json.Marshal(&sms)
	a.NoError(err)

	msg := protocol.Message{
		Path: protocol.Path(topic),
		ID:   uint64(4),
		Body: d,
	}

	mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Return(nil)
	a.NotNil(gw.route)
	gw.route.Deliver(&msg)
	time.Sleep(100 * time.Millisecond)

	err = gw.Stop()
	a.NoError(err)

	err = gw.ReadLastID()
	a.NoError(err)

	time.Sleep(100 * time.Millisecond)
}

func Test_Restart(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()
	defer testutil.EnableDebugForMethod()()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)
	msgStore := NewMockMessageStore(ctrl)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)

	topic := "/sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *router.Route) (*router.Route, error) {
		a.Equal(strings.Split(topic, "/")[1], r.Path.Partition())
		return r, nil
	}).Times(2)

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	err = gw.Start()
	a.NoError(err)

	sms := NexmoSms{
		To:   "toNumber",
		From: "FromNUmber",
		Text: "body",
	}
	d, err := json.Marshal(&sms)
	a.NoError(err)

	msg := protocol.Message{
		Path:          protocol.Path(topic),
		UserID:        "samsa",
		ApplicationID: "sms",
		ID:            uint64(4),
		Body:          d,
	}

	//TODO MARIAN  FIX THIS TEST
	//msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(0), nil)
	//msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(4), nil)
	//msgStore.EXPECT().MaxMessageID(gomock.Eq(gw.route.Path.Partition())).Return(uint64(4), nil)
	mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Times(1).Return(ErrNoSMSSent)

	//routerMock.EXPECT().Fetch(gomock.Any()).Do(func(r *store.FetchRequest) {
	//	go func() {
	//
	//		logger.WithField("r.Partition", r.Partition).Info("----")
	//
	//		a.Equal(strings.Split(topic, "/")[1], r.Partition)
	//
	//		r.StartC <- 1
	//
	//		r.MessageC <- &store.FetchedMessage{ID: uint64(4), Message: msg.Bytes()}
	//		close(r.MessageC)
	//	}()
	//})
	doneC := make(chan bool)
	routerMock.EXPECT().Done().AnyTimes().Return(doneC)
	//
	//mockSmsSender.EXPECT().Send(gomock.Eq(&msg)).Return(nil)

	a.NotNil(gw.route)
	gw.route.Deliver(&msg)
	time.Sleep(100 * time.Millisecond)
}

func TestReadLastID(t *testing.T) {
	ctrl, finish := testutil.NewMockCtrl(t)
	defer finish()

	defer testutil.EnableDebugForMethod()()
	a := assert.New(t)

	mockSmsSender := NewMockSender(ctrl)
	kvStore := kvstore.NewMemoryKVStore()

	a.NotNil(kvStore)
	routerMock := NewMockRouter(testutil.MockCtrl)
	routerMock.EXPECT().KVStore().AnyTimes().Return(kvStore, nil)
	msgStore := dummystore.New(kvStore)
	routerMock.EXPECT().MessageStore().AnyTimes().Return(msgStore, nil)

	topic := "/sms"
	worker := 1
	config := Config{
		Workers:  &worker,
		SMSTopic: &topic,
		Name:     "test_gateway",
		Schema:   SMSSchema,
	}

	gw, err := New(routerMock, mockSmsSender, config)
	a.NoError(err)

	gw.SetLastSentID(uint64(10))

	gw.ReadLastID()

	a.Equal(uint64(10), gw.LastIDSent)
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
)

// PositionHandlers handles all request to positon management
type PositionHandlers struct {
	*Context
	validator    StructValidator
	errorHandler errorHandlerFunc
}

// NewPositionHandlers creates a new position handlers object
func NewPositionHandlers(context *Context) *PositionHandlers {
	return &PositionHandlers{
		Context:      context,
		validator:    validator.New(),
		errorHandler: handleError,
	}
}

// AddPosition handles http request to save a position
//
// This function is a handler for http server, it should not be called directly
func (handlers *PositionHandlers) AddPosition(c echo.Context) error {
	position := new(Position)
	if err := c.Bind(position); err != nil {
		return handlers.errorHandler(c, http.StatusBadRequest, err)
	}
	if err := handlers.validator.Struct(position); err != nil {
		return handlers.errorHandler(c, http.StatusBadRequest, err)
	}
	if err := handlers.Context.esPosition.AddPosition(position); err != nil {
		return handlers.errorHandler(c, http.StatusBadRequest, err)
	}
	return c.JSON(http.StatusOK, position)
}
<commit_msg>fix spelling in position handlers comments<commit_after>package main

import (
	"net/http"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
)

// PositionHandlers handles all request to position management
type PositionHandlers struct {
	*Context
	validator    StructValidator
	errorHandler errorHandlerFunc
}

// NewPositionHandlers creates a new position handlers object
func NewPositionHandlers(context *Context) *PositionHandlers {
	return &PositionHandlers{
		Context:      context,
		validator:    validator.New(),
		errorHandler: handleError,
	}
}

// AddPosition handles http request to save a position
//
// This function is a handler for http server, it should not be called directly
func (handlers *PositionHandlers) AddPosition(c echo.Context) error {
	position := new(Position)
	if err := c.Bind(position); err != nil {
		return handlers.errorHandler(c, http.StatusBadRequest, err)
	}
	if err := handlers.validator.Struct(position); err != nil {
		return handlers.errorHandler(c, http.StatusBadRequest, err)
	}
	if err := handlers.Context.esPosition.AddPosition(position); err != nil {
		return handlers.errorHandler(c, http.StatusBadRequest, err)
	}
	return c.JSON(http.StatusOK, position)
}
<|endoftext|>


<commit_before>package postgis

import (
	"github.com/aodin/aspect"
)

// Returns the area of the given column
// For "geometry" type area is in SRID units.
// For "geography" area is in square meters.
func AreaOf(c aspect.ColumnElem) aspect.ColumnElem {
	return c.SetInner(
		aspect.FuncClause{Inner: c.Inner(), F: "ST_Area"},
	)
}

// Returns the area of the surface if it is a polygon or multi-polygon.
// For "geometry" type area is in SRID units.
// For "geography" area is in square meters.
func Area(s Shape) aspect.Clause {
	return aspect.FuncClause{
		Inner: s,
		F:     "ST_Area",
	}
}

// Returns true if the given geometry is completely inside the given column
func Within(c aspect.ColumnElem, s Shape) aspect.Clause {
	return aspect.FuncClause{
		Inner: aspect.ArrayClause{Clauses: []aspect.Clause{s, c}, Sep: ", "},
		F:     "ST_Within",
	}
}

// ST_Intersects — Returns TRUE if the Geometries/Geography "spatially
// intersect in 2D" - (share any portion of space) and FALSE if they don't
// (they are Disjoint). For geography -- tolerance is 0.00001 meters (so any
// points that close are considered to intersect)
func Intersects(c aspect.ColumnElem, s Shape) aspect.Clause {
	return aspect.FuncClause{
		Inner: aspect.ArrayClause{Clauses: []aspect.Clause{s, c}, Sep: ", "},
		F:     "ST_Intersects",
	}
}
<commit_msg>Added PostGIS DWithin function<commit_after>package postgis

import (
	"github.com/aodin/aspect"
)

// Returns the area of the given column
// For "geometry" type area is in SRID units.
// For "geography" area is in square meters.
func AreaOf(c aspect.ColumnElem) aspect.ColumnElem {
	return c.SetInner(
		aspect.FuncClause{Inner: c.Inner(), F: "ST_Area"},
	)
}

// Returns the area of the surface if it is a polygon or multi-polygon.
// For "geometry" type area is in SRID units.
// For "geography" area is in square meters.
func Area(s Shape) aspect.Clause {
	return aspect.FuncClause{
		Inner: s,
		F:     "ST_Area",
	}
}

// Within returns true if the given shape is completely inside the given column
func Within(c aspect.ColumnElem, s Shape) aspect.Clause {
	return aspect.FuncClause{
		Inner: aspect.ArrayClause{Clauses: []aspect.Clause{s, c}, Sep: ", "},
		F:     "ST_Within",
	}
}

func DWithin(c aspect.ColumnElem, s Shape, d int) aspect.Clause {
	return aspect.FuncClause{
		Inner: aspect.ArrayClause{
			Clauses: []aspect.Clause{s, c, aspect.IntClause{d}},
			Sep:     ", ",
		},
		F: "ST_DWithin",
	}
}

// ST_Intersects — Returns TRUE if the Geometries/Geography "spatially
// intersect in 2D" - (share any portion of space) and FALSE if they don't
// (they are Disjoint). For geography -- tolerance is 0.00001 meters (so any
// points that close are considered to intersect)
func Intersects(c aspect.ColumnElem, s Shape) aspect.Clause {
	return aspect.FuncClause{
		Inner: aspect.ArrayClause{Clauses: []aspect.Clause{s, c}, Sep: ", "},
		F:     "ST_Intersects",
	}
}
<|endoftext|>


<commit_before>package docker

import (
	"context"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/pkg/errors"
	"github.com/pterodactyl/wings/api"
	"github.com/pterodactyl/wings/environment"
	"github.com/pterodactyl/wings/events"
	"io"
	"sync"
)

type Metadata struct {
	Image string
	Stop  api.ProcessStopConfiguration
}

// Ensure that the Docker environment is always implementing all of the methods
// from the base environment interface.
var _ environment.ProcessEnvironment = (*Environment)(nil)

type Environment struct {
	mu      sync.RWMutex
	eventMu sync.Mutex

	// The public identifier for this environment. In this case it is the Docker container
	// name that will be used for all instances created under it.
	Id string

	// The environment configuration.
	Configuration *environment.Configuration

	meta *Metadata

	// The Docker client being used for this instance.
	client *client.Client

	// Controls the hijacked response stream which exists only when we're attached to
	// the running container instance.
	stream *types.HijackedResponse

	// Holds the stats stream used by the polling commands so that we can easily close it out.
	stats io.ReadCloser

	emitter *events.EventBus

	// Tracks the environment state.
	st   string
	stMu sync.RWMutex
}

// Creates a new base Docker environment. The ID passed through will be the ID that is used to
// reference the container from here on out. This should be unique per-server (we use the UUID
// by default). The container does not need to exist at this point.
func New(id string, m *Metadata, c *environment.Configuration) (*Environment, error) {
	cli, err := environment.DockerClient()
	if err != nil {
		return nil, err
	}

	e := &Environment{
		Id:            id,
		Configuration: c,
		meta:          m,
		client:        cli,
	}

	return e, nil
}

func (e *Environment) Type() string {
	return "docker"
}

// Set if this process is currently attached to the process.
func (e *Environment) SetStream(s *types.HijackedResponse) {
	e.mu.Lock()
	e.stream = s
	e.mu.Unlock()
}

// Determine if the this process is currently attached to the container.
func (e *Environment) IsAttached() bool {
	e.mu.RLock()
	defer e.mu.RUnlock()

	return e.stream != nil
}

func (e *Environment) Events() *events.EventBus {
	e.eventMu.Lock()
	defer e.eventMu.Unlock()

	if e.emitter == nil {
		e.emitter = events.New()
	}

	return e.emitter
}

// Determines if the container exists in this environment. The ID passed through should be the
// server UUID since containers are created utilizing the server UUID as the name and docker
// will work fine when using the container name as the lookup parameter in addition to the longer
// ID auto-assigned when the container is created.
func (e *Environment) Exists() (bool, error) {
	_, err := e.client.ContainerInspect(context.Background(), e.Id)

	if err != nil {
		// If this error is because the container instance wasn't found via Docker we
		// can safely ignore the error and just return false.
		if client.IsErrNotFound(err) {
			return false, nil
		}

		return false, err
	}

	return true, nil
}

// Determines if the server's docker container is currently running. If there is no container
// present, an error will be raised (since this shouldn't be a case that ever happens under
// correctly developed circumstances).
//
// You can confirm if the instance wasn't found by using client.IsErrNotFound from the Docker
// API.
//
// @see docker/client/errors.go
func (e *Environment) IsRunning() (bool, error) {
	c, err := e.client.ContainerInspect(context.Background(), e.Id)
	if err != nil {
		return false, err
	}

	return c.State.Running, nil
}

// Determine the container exit state and return the exit code and whether or not
// the container was killed by the OOM killer.
func (e *Environment) ExitState() (uint32, bool, error) {
	c, err := e.client.ContainerInspect(context.Background(), e.Id)
	if err != nil {
		// I'm not entirely sure how this can happen to be honest. I tried deleting a
		// container _while_ a server was running and wings gracefully saw the crash and
		// created a new container for it.
		//
		// However, someone reported an error in Discord about this scenario happening,
		// so I guess this should prevent it? They didn't tell me how they caused it though
		// so that's a mystery that will have to go unsolved.
		//
		// @see https://github.com/pterodactyl/panel/issues/2003
		if client.IsErrNotFound(err) {
			return 1, false, nil
		}

		return 0, false, errors.WithStack(err)
	}

	return uint32(c.State.ExitCode), c.State.OOMKilled, nil
}

// Returns the environment configuration allowing a process to make modifications of the
// environment on the fly.
func (e *Environment) Config() *environment.Configuration {
	e.mu.RLock()
	defer e.mu.RUnlock()

	return e.Configuration
}

// Sets the stop configuration for the environment.
func (e *Environment) SetStopConfiguration(c api.ProcessStopConfiguration) {
	e.mu.Lock()
	e.meta.Stop = c
	e.mu.Unlock()
}

func (e *Environment) SetImage(i string) {
	e.mu.Lock()
	e.meta.Image = i
	e.mu.Unlock()
}
<commit_msg>Ensure environment starts in default offline state, rather than an empty string; closes pterodactyl/panel#2519<commit_after>package docker

import (
	"context"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/pkg/errors"
	"github.com/pterodactyl/wings/api"
	"github.com/pterodactyl/wings/environment"
	"github.com/pterodactyl/wings/events"
	"io"
	"sync"
)

type Metadata struct {
	Image string
	Stop  api.ProcessStopConfiguration
}

// Ensure that the Docker environment is always implementing all of the methods
// from the base environment interface.
var _ environment.ProcessEnvironment = (*Environment)(nil)

type Environment struct {
	mu      sync.RWMutex
	eventMu sync.Mutex

	// The public identifier for this environment. In this case it is the Docker container
	// name that will be used for all instances created under it.
	Id string

	// The environment configuration.
	Configuration *environment.Configuration

	meta *Metadata

	// The Docker client being used for this instance.
	client *client.Client

	// Controls the hijacked response stream which exists only when we're attached to
	// the running container instance.
	stream *types.HijackedResponse

	// Holds the stats stream used by the polling commands so that we can easily close it out.
	stats io.ReadCloser

	emitter *events.EventBus

	// Tracks the environment state.
	st   string
	stMu sync.RWMutex
}

// Creates a new base Docker environment. The ID passed through will be the ID that is used to
// reference the container from here on out. This should be unique per-server (we use the UUID
// by default). The container does not need to exist at this point.
func New(id string, m *Metadata, c *environment.Configuration) (*Environment, error) {
	cli, err := environment.DockerClient()
	if err != nil {
		return nil, err
	}

	e := &Environment{
		Id:            id,
		Configuration: c,
		meta:          m,
		client:        cli,
		st:            environment.ProcessOfflineState,
	}

	return e, nil
}

func (e *Environment) Type() string {
	return "docker"
}

// Set if this process is currently attached to the process.
func (e *Environment) SetStream(s *types.HijackedResponse) {
	e.mu.Lock()
	e.stream = s
	e.mu.Unlock()
}

// Determine if the this process is currently attached to the container.
func (e *Environment) IsAttached() bool {
	e.mu.RLock()
	defer e.mu.RUnlock()

	return e.stream != nil
}

func (e *Environment) Events() *events.EventBus {
	e.eventMu.Lock()
	defer e.eventMu.Unlock()

	if e.emitter == nil {
		e.emitter = events.New()
	}

	return e.emitter
}

// Determines if the container exists in this environment. The ID passed through should be the
// server UUID since containers are created utilizing the server UUID as the name and docker
// will work fine when using the container name as the lookup parameter in addition to the longer
// ID auto-assigned when the container is created.
func (e *Environment) Exists() (bool, error) {
	_, err := e.client.ContainerInspect(context.Background(), e.Id)

	if err != nil {
		// If this error is because the container instance wasn't found via Docker we
		// can safely ignore the error and just return false.
		if client.IsErrNotFound(err) {
			return false, nil
		}

		return false, err
	}

	return true, nil
}

// Determines if the server's docker container is currently running. If there is no container
// present, an error will be raised (since this shouldn't be a case that ever happens under
// correctly developed circumstances).
//
// You can confirm if the instance wasn't found by using client.IsErrNotFound from the Docker
// API.
//
// @see docker/client/errors.go
func (e *Environment) IsRunning() (bool, error) {
	c, err := e.client.ContainerInspect(context.Background(), e.Id)
	if err != nil {
		return false, err
	}

	return c.State.Running, nil
}

// Determine the container exit state and return the exit code and whether or not
// the container was killed by the OOM killer.
func (e *Environment) ExitState() (uint32, bool, error) {
	c, err := e.client.ContainerInspect(context.Background(), e.Id)
	if err != nil {
		// I'm not entirely sure how this can happen to be honest. I tried deleting a
		// container _while_ a server was running and wings gracefully saw the crash and
		// created a new container for it.
		//
		// However, someone reported an error in Discord about this scenario happening,
		// so I guess this should prevent it? They didn't tell me how they caused it though
		// so that's a mystery that will have to go unsolved.
		//
		// @see https://github.com/pterodactyl/panel/issues/2003
		if client.IsErrNotFound(err) {
			return 1, false, nil
		}

		return 0, false, errors.WithStack(err)
	}

	return uint32(c.State.ExitCode), c.State.OOMKilled, nil
}

// Returns the environment configuration allowing a process to make modifications of the
// environment on the fly.
func (e *Environment) Config() *environment.Configuration {
	e.mu.RLock()
	defer e.mu.RUnlock()

	return e.Configuration
}

// Sets the stop configuration for the environment.
func (e *Environment) SetStopConfiguration(c api.ProcessStopConfiguration) {
	e.mu.Lock()
	e.meta.Stop = c
	e.mu.Unlock()
}

func (e *Environment) SetImage(i string) {
	e.mu.Lock()
	e.meta.Image = i
	e.mu.Unlock()
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"net/url"
	"strings"
)

//ConfigMode is the final, derived struct holding all of the leaf values in
//config.
type ConfigMode struct {
	//ConfigMode is primarily just the common config mode values
	ConfigModeCommon
	//Games is not intended to be inflated from JSON, but rather is derived
	//based on the contents of Games. It is OK to use literally as Games in
	//RawConfig, though, because its serialization is a legal GamesNode.
	Games []string `json:"games"`

	parentConfig *Config
}

func (c *ConfigMode) String() string {
	blob, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		return "ERROR, couldn't unmarshal: " + err.Error()
	}
	return string(blob)
}

//ParentConfig returns the Config that this ConfigMode is part of.
//Specifically, returns the config that was passed as ParentConfig to
//RawConfigMode.Derive().
func (c *ConfigMode) ParentConfig() *Config {
	return c.parentConfig
}

func (c *ConfigMode) OriginAllowed(origin string) bool {

	originUrl, err := url.Parse(origin)

	if err != nil {
		return false
	}

	if c.AllowedOrigins == "" {
		return false
	}
	if c.AllowedOrigins == "*" {
		return true
	}
	allowedOrigins := strings.Split(c.AllowedOrigins, ",")
	for _, allowedOrigin := range allowedOrigins {
		u, err := url.Parse(allowedOrigin)

		if err != nil {
			continue
		}

		if u.Scheme == originUrl.Scheme && u.Host == originUrl.Host {
			return true
		}
	}
	return false
}
<commit_msg>Add ConfigMode.AllGamePackages and ConfigMode.GamePackages, which pass the location of config.json's dir as the base path. Part of #694.<commit_after>package config

import (
	"encoding/json"
	"github.com/jkomoros/boardgame/boardgame-util/lib/gamepkg"
	"net/url"
	"path/filepath"
	"strings"
)

//ConfigMode is the final, derived struct holding all of the leaf values in
//config.
type ConfigMode struct {
	//ConfigMode is primarily just the common config mode values
	ConfigModeCommon
	//Games is not intended to be inflated from JSON, but rather is derived
	//based on the contents of Games. It is OK to use literally as Games in
	//RawConfig, though, because its serialization is a legal GamesNode.
	Games []string `json:"games"`

	parentConfig *Config
}

func (c *ConfigMode) String() string {
	blob, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		return "ERROR, couldn't unmarshal: " + err.Error()
	}
	return string(blob)
}

//GamePackages returns all of the game packages listed in Games[] that are
//valid, with errors for the invalid ones. A wrapper around
//gamepkg.Packages(), that passes the path of the config as optionalBasePath,
//so that relative paths in games listed in config are interpreted as relative
//to the config.json, not whatever working directory boardgame-util is being
//run in.
func (c *ConfigMode) GamePackages() ([]*gamepkg.Pkg, map[string]error) {

	return gamepkg.Packages(c.Games, c.basePath())

}

//AllGamePackages returns either a gamepkg for each listed game, or an error
//if any one of them was invalid. A wrapper around gamepkg.AllPackages(), that
//passes the path of the config as optionalBasePath, so that relative paths in
//games listed in config are interpreted as relative to the config.json, not
//whatever working directory boardgame-util is being run in.
func (c *ConfigMode) AllGamePackages() ([]*gamepkg.Pkg, error) {
	return gamepkg.AllPackages(c.Games, c.basePath())
}

//basePath returns the base path to pass to gamepkg.Packages and friends.
func (c *ConfigMode) basePath() string {
	if c.parentConfig == nil {
		return ""
	}

	path := c.parentConfig.Path()

	if path == "" {
		path = c.parentConfig.SecretPath()
	}

	if path == "" {
		return path
	}

	return filepath.Dir(path)

}

//ParentConfig returns the Config that this ConfigMode is part of.
//Specifically, returns the config that was passed as ParentConfig to
//RawConfigMode.Derive().
func (c *ConfigMode) ParentConfig() *Config {
	return c.parentConfig
}

func (c *ConfigMode) OriginAllowed(origin string) bool {

	originUrl, err := url.Parse(origin)

	if err != nil {
		return false
	}

	if c.AllowedOrigins == "" {
		return false
	}
	if c.AllowedOrigins == "*" {
		return true
	}
	allowedOrigins := strings.Split(c.AllowedOrigins, ",")
	for _, allowedOrigin := range allowedOrigins {
		u, err := url.Parse(allowedOrigin)

		if err != nil {
			continue
		}

		if u.Scheme == originUrl.Scheme && u.Host == originUrl.Host {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Pani Networks
// 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 listener listens to Kubernetes for policy updates.
package listener

import (
	"os"

	"github.com/romana/core/common"
	"github.com/romana/core/common/api"
	"github.com/romana/core/common/client"
	log "github.com/romana/rlog"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

const (
	defaultSegmentLabelName = "tier"
	defaultTenantLabelName  = "namespace"
)

// KubeListener is a Service that listens to updates
// from Kubernetes by connecting to the endpoints specified
// and consuming chunked JSON documents. The endpoints are
// constructed from kubeURL and the following paths:
// 1. namespaceNotificationPath for namespace additions/deletions
// 2. policyNotificationPathPrefix + <namespace name> + policyNotificationPathPostfix
//    for policy additions/deletions.
type KubeListener struct {
	Addr   string
	client *client.Client

	segmentLabelName              string
	tenantLabelName               string
	namespaceBufferSize           uint64

	lastEventPerNamespace map[string]uint64

	kubeClientSet *kubernetes.Clientset
	Watchers      map[string]cache.ListerWatcher
}

// Routes returns various routes used in the service.
func (l *KubeListener) Routes() common.Routes {
	routes := common.Routes{}
	return routes
}

func (l *KubeListener) GetAddress() string {
	return l.Addr
}

// Name implements method of Service interface.
func (l *KubeListener) Name() string {
	return "kubernetesListener"
}

func (l *KubeListener) loadConfig() error {
	var err error
	configPrefix := "/kubelistener/config/"

	l.segmentLabelName, err = l.client.Store.GetString(configPrefix+"segmentLabelName", defaultSegmentLabelName)
	if err != nil {
		return err
	}

	l.tenantLabelName, err = l.client.Store.GetString(configPrefix+"tenantLabelName", defaultTenantLabelName)
	if err != nil {
		return err
	}

	return nil

}

// TODO there should be a better way to introduce translator
// then global variable like this one.
var PTranslator Translator

// addNetworkPolicy adds the policy to the policy service.
func (l *KubeListener) addNetworkPolicy(policy api.Policy) error {
	return l.client.AddPolicy(policy)
}

func (l *KubeListener) Initialize(clientConfig common.Config) error {
	var err error
	l.client, err = client.NewClient(&clientConfig)
	if err != nil {
		return err
	}
	err = l.loadConfig()
	if err != nil {
		return err
	}

	// TODO, find a better place to initialize
	// the translator. Stas.
	PTranslator.Init(l.client, l.segmentLabelName, l.tenantLabelName)
	tc := PTranslator.GetClient()
	if tc == nil {
		log.Critical("Failed to initialize rest client for policy translator.")
		os.Exit(255)
	}

	// Channel for stopping watching kubernetes events.
	done := make(chan struct{})

	// l.ProcessNodeEvents listens and processes kubernetes node events,
	// mainly allowing nodes to be added/removed to/from romana cluster
	// based on these events.
	l.ProcessNodeEvents(done)

	l.lastEventPerNamespace = make(map[string]uint64)
	log.Infof("%s: Starting server", l.Name())
	eventc, err := l.nsWatch(done)
	if err != nil {
		log.Critical("Namespace watcher failed to start", err)
		os.Exit(255)
	}

	l.process(eventc, done)

	ProduceNewPolicyEvents(eventc, done, l)

	l.startRomanaIPSync(done)

	log.Info("All routines started")
	return nil
}
<commit_msg>Re-run gofmt / goimports<commit_after>// Copyright (c) 2016 Pani Networks
// 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 listener listens to Kubernetes for policy updates.
package listener

import (
	"os"

	"github.com/romana/core/common"
	"github.com/romana/core/common/api"
	"github.com/romana/core/common/client"
	log "github.com/romana/rlog"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

const (
	defaultSegmentLabelName = "tier"
	defaultTenantLabelName  = "namespace"
)

// KubeListener is a Service that listens to updates
// from Kubernetes by connecting to the endpoints specified
// and consuming chunked JSON documents. The endpoints are
// constructed from kubeURL and the following paths:
// 1. namespaceNotificationPath for namespace additions/deletions
// 2. policyNotificationPathPrefix + <namespace name> + policyNotificationPathPostfix
//    for policy additions/deletions.
type KubeListener struct {
	Addr   string
	client *client.Client

	segmentLabelName    string
	tenantLabelName     string
	namespaceBufferSize uint64

	lastEventPerNamespace map[string]uint64

	kubeClientSet *kubernetes.Clientset
	Watchers      map[string]cache.ListerWatcher
}

// Routes returns various routes used in the service.
func (l *KubeListener) Routes() common.Routes {
	routes := common.Routes{}
	return routes
}

func (l *KubeListener) GetAddress() string {
	return l.Addr
}

// Name implements method of Service interface.
func (l *KubeListener) Name() string {
	return "kubernetesListener"
}

func (l *KubeListener) loadConfig() error {
	var err error
	configPrefix := "/kubelistener/config/"

	l.segmentLabelName, err = l.client.Store.GetString(configPrefix+"segmentLabelName", defaultSegmentLabelName)
	if err != nil {
		return err
	}

	l.tenantLabelName, err = l.client.Store.GetString(configPrefix+"tenantLabelName", defaultTenantLabelName)
	if err != nil {
		return err
	}

	return nil

}

// TODO there should be a better way to introduce translator
// then global variable like this one.
var PTranslator Translator

// addNetworkPolicy adds the policy to the policy service.
func (l *KubeListener) addNetworkPolicy(policy api.Policy) error {
	return l.client.AddPolicy(policy)
}

func (l *KubeListener) Initialize(clientConfig common.Config) error {
	var err error
	l.client, err = client.NewClient(&clientConfig)
	if err != nil {
		return err
	}
	err = l.loadConfig()
	if err != nil {
		return err
	}

	// TODO, find a better place to initialize
	// the translator. Stas.
	PTranslator.Init(l.client, l.segmentLabelName, l.tenantLabelName)
	tc := PTranslator.GetClient()
	if tc == nil {
		log.Critical("Failed to initialize rest client for policy translator.")
		os.Exit(255)
	}

	// Channel for stopping watching kubernetes events.
	done := make(chan struct{})

	// l.ProcessNodeEvents listens and processes kubernetes node events,
	// mainly allowing nodes to be added/removed to/from romana cluster
	// based on these events.
	l.ProcessNodeEvents(done)

	l.lastEventPerNamespace = make(map[string]uint64)
	log.Infof("%s: Starting server", l.Name())
	eventc, err := l.nsWatch(done)
	if err != nil {
		log.Critical("Namespace watcher failed to start", err)
		os.Exit(255)
	}

	l.process(eventc, done)

	ProduceNewPolicyEvents(eventc, done, l)

	l.startRomanaIPSync(done)

	log.Info("All routines started")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/url"
	"flag"

	common "github.com/tb0hdan/torpedo_common"
	"github.com/tb0hdan/torpedo_registry"
)

type SoundCloudSearchResponseUser struct {
	ID           int64  `json:"id"`
	AvatarURL    string `json:"avatar_url"`
	Kind         string `json:"kind"`
	LastModified string `json:"last_modified"`
	Permalink    string `json:"permalink"`
	PermalinkURL string `json:"permalink_url"`
	URI          string `json:"uri"`
	UserName     string `json:"username"`
}

type SoundCloudSearchResponse struct {
	User                *SoundCloudSearchResponseUser `json:"user"`
	ID                  int64                         `json:"id"`
	AttachmentsURI      string                        `json:"attachments_uri"`
	BPM                 string                        `json:"bpm"`
	Kind                string                        `json:"kind"`
	CreatedAt           string                        `json:"created_at"`
	LastModified        string                        `json:"last_modified"`
	Permalink           string                        `json:"permalink"`
	PermalinkURL        string                        `json:"permalink_url"`
	Title               string                        `json:"title"`
	Duration            int64                         `json:"duration"`
	Sharing             string                        `json:"sharing"`
	WaveformURL         string                        `json:"waveform_url"`
	StreamURL           string                        `json:"stream_url"`
	URI                 string                        `json:"uri"`
	UserID              int64                         `json:"user_id"`
	ArtworkURL          string                        `json:"artwork_url"`
	CommentCount        int64                         `json:"comment_count"`
	Commentable         bool                          `json:"commentable"`
	Description         string                        `json:"description"`
	DownloadCount       int64                         `json:"download_count"`
	Downloadable        bool                          `json:"downloadable"`
	EmbeddableBy        string                        `json:"embeddable_by"`
	Favoritings_count   int64                         `json:"favoritings_count"`
	Genre               string                        `json:"genre"`
	ISRC                string                        `json:"isrc"`
	LabelID             string                        `json:"label_id"`
	LabelName           string                        `json:"label_name"`
	License             string                        `json:"license"`
	LikesCount          int64                         `json:"likes_count"`
	OriginalContentSize int64                         `json:"original_content_size"`
	OriginalFormat      string                        `json:"original_format"`
	PlaybackCount       int64                         `json:"playback_count"`
	PurchaseTitle       string                        `json:"purchase_title"`
	PurchaseURL         string                        `json:"purchase_url"`
	Release             string                        `json:"release"`
	ReleaseDay          string                        `json:"release_day"`
	ReleaseMonth        string                        `json:"release_month"`
	ReleaseYear         string                        `json:"release_year"`
	RepostsCount        int64                         `json:"reposts_count"`
	State               string                        `json:"state"`
	Streamable          bool                          `json:"streamable"`
	TagList             string                        `json:"tag_list"`
	TrackType           string                        `json:"track_type"`
	KeySignature        string                        `json:"key_signature"`
	UserFaforite        bool                          `json:"user_favorite"`
	UserPlaybackCount   int64                         `json:"user_playback_count"`
	VideoURL            string                        `json:"video_url"`
	DownloadURL         string                        `json:"download_url"`
}

var SoundCloudClientID *string

func ConfigureSoundCloudPlugin(cfg *torpedo_registry.ConfigStruct) {
	SoundCloudClientID = flag.String("soundcloud_id", "", "SoundCloud client ID")

}

func ParseSoundCloudPlugin(cfg *torpedo_registry.ConfigStruct) {
	cfg.SetConfig("soundcloudclientid", *SoundCloudClientID)
	if cfg.GetConfig()["soundcloudclientid"] == "" {
		cfg.SetConfig("soundcloudclientid", common.GetStripEnv("SOUNDCLOUD_ID"))
	}
}

func SoundCloudProcessMessage(api *torpedo_registry.BotAPI, channel interface{}, incoming_message string) {
	var message string
	cu := &common.Utils{}
	logger := cu.NewLog("soundcloud-process-message")
	_, command, _ := common.GetRequestedFeature(incoming_message)
	result := make([]SoundCloudSearchResponse, 0)
	query := url.Values{}
	query.Add("client_id", torpedo_registry.Config.GetConfig()["soundcloudclientid"])
	query.Add("q", url.QueryEscape(command))
	url := "https://api.soundcloud.com/tracks/?" + query.Encode()
	err := cu.GetURLUnmarshal(url, &result)
	if err != nil {
		message = "An error occured while searching for track on SoundCloud"
		logger.Printf("SoundCloud error: %+v, %s\n", err, url)
		api.Bot.PostMessage(channel, message, api)
		return
	}
	message = result[0].PermalinkURL
	api.Bot.PostMessage(channel, message, api)
}

func init() {
	helpmsg :=  "Search for track on SoundCloud"
	torpedo_registry.Config.RegisterHandler("soundcloud", SoundCloudProcessMessage)
	torpedo_registry.Config.RegisterHelp("soundcloud", helpmsg)
	torpedo_registry.Config.RegisterHandler("sc", SoundCloudProcessMessage)
	torpedo_registry.Config.RegisterHelp("sc", helpmsg)
	torpedo_registry.Config.RegisterPreParser("soundcloud", ConfigureSoundCloudPlugin)
	torpedo_registry.Config.RegisterPostParser("soundcloud", ParseSoundCloudPlugin)
}
<commit_msg>soundcloud plugin<commit_after>package torpedo_soundcloud_plugin

import (
	"net/url"
	"flag"

	common "github.com/tb0hdan/torpedo_common"
	"github.com/tb0hdan/torpedo_registry"
)

type SoundCloudSearchResponseUser struct {
	ID           int64  `json:"id"`
	AvatarURL    string `json:"avatar_url"`
	Kind         string `json:"kind"`
	LastModified string `json:"last_modified"`
	Permalink    string `json:"permalink"`
	PermalinkURL string `json:"permalink_url"`
	URI          string `json:"uri"`
	UserName     string `json:"username"`
}

type SoundCloudSearchResponse struct {
	User                *SoundCloudSearchResponseUser `json:"user"`
	ID                  int64                         `json:"id"`
	AttachmentsURI      string                        `json:"attachments_uri"`
	BPM                 string                        `json:"bpm"`
	Kind                string                        `json:"kind"`
	CreatedAt           string                        `json:"created_at"`
	LastModified        string                        `json:"last_modified"`
	Permalink           string                        `json:"permalink"`
	PermalinkURL        string                        `json:"permalink_url"`
	Title               string                        `json:"title"`
	Duration            int64                         `json:"duration"`
	Sharing             string                        `json:"sharing"`
	WaveformURL         string                        `json:"waveform_url"`
	StreamURL           string                        `json:"stream_url"`
	URI                 string                        `json:"uri"`
	UserID              int64                         `json:"user_id"`
	ArtworkURL          string                        `json:"artwork_url"`
	CommentCount        int64                         `json:"comment_count"`
	Commentable         bool                          `json:"commentable"`
	Description         string                        `json:"description"`
	DownloadCount       int64                         `json:"download_count"`
	Downloadable        bool                          `json:"downloadable"`
	EmbeddableBy        string                        `json:"embeddable_by"`
	Favoritings_count   int64                         `json:"favoritings_count"`
	Genre               string                        `json:"genre"`
	ISRC                string                        `json:"isrc"`
	LabelID             string                        `json:"label_id"`
	LabelName           string                        `json:"label_name"`
	License             string                        `json:"license"`
	LikesCount          int64                         `json:"likes_count"`
	OriginalContentSize int64                         `json:"original_content_size"`
	OriginalFormat      string                        `json:"original_format"`
	PlaybackCount       int64                         `json:"playback_count"`
	PurchaseTitle       string                        `json:"purchase_title"`
	PurchaseURL         string                        `json:"purchase_url"`
	Release             string                        `json:"release"`
	ReleaseDay          string                        `json:"release_day"`
	ReleaseMonth        string                        `json:"release_month"`
	ReleaseYear         string                        `json:"release_year"`
	RepostsCount        int64                         `json:"reposts_count"`
	State               string                        `json:"state"`
	Streamable          bool                          `json:"streamable"`
	TagList             string                        `json:"tag_list"`
	TrackType           string                        `json:"track_type"`
	KeySignature        string                        `json:"key_signature"`
	UserFaforite        bool                          `json:"user_favorite"`
	UserPlaybackCount   int64                         `json:"user_playback_count"`
	VideoURL            string                        `json:"video_url"`
	DownloadURL         string                        `json:"download_url"`
}

var SoundCloudClientID *string

func ConfigureSoundCloudPlugin(cfg *torpedo_registry.ConfigStruct) {
	SoundCloudClientID = flag.String("soundcloud_id", "", "SoundCloud client ID")

}

func ParseSoundCloudPlugin(cfg *torpedo_registry.ConfigStruct) {
	cfg.SetConfig("soundcloudclientid", *SoundCloudClientID)
	if cfg.GetConfig()["soundcloudclientid"] == "" {
		cfg.SetConfig("soundcloudclientid", common.GetStripEnv("SOUNDCLOUD_ID"))
	}
}

func SoundCloudProcessMessage(api *torpedo_registry.BotAPI, channel interface{}, incoming_message string) {
	var message string
	cu := &common.Utils{}
	logger := cu.NewLog("soundcloud-process-message")
	_, command, _ := common.GetRequestedFeature(incoming_message)
	result := make([]SoundCloudSearchResponse, 0)
	query := url.Values{}
	query.Add("client_id", torpedo_registry.Config.GetConfig()["soundcloudclientid"])
	query.Add("q", url.QueryEscape(command))
	url := "https://api.soundcloud.com/tracks/?" + query.Encode()
	err := cu.GetURLUnmarshal(url, &result)
	if err != nil {
		message = "An error occured while searching for track on SoundCloud"
		logger.Printf("SoundCloud error: %+v, %s\n", err, url)
		api.Bot.PostMessage(channel, message, api)
		return
	}
	message = result[0].PermalinkURL
	api.Bot.PostMessage(channel, message, api)
}

func init() {
	helpmsg :=  "Search for track on SoundCloud"
	torpedo_registry.Config.RegisterHandler("soundcloud", SoundCloudProcessMessage)
	torpedo_registry.Config.RegisterHelp("soundcloud", helpmsg)
	torpedo_registry.Config.RegisterHandler("sc", SoundCloudProcessMessage)
	torpedo_registry.Config.RegisterHelp("sc", helpmsg)
	torpedo_registry.Config.RegisterPreParser("soundcloud", ConfigureSoundCloudPlugin)
	torpedo_registry.Config.RegisterPostParser("soundcloud", ParseSoundCloudPlugin)
}
<|endoftext|>


<commit_before>package openstack

import (
	"fmt"
	"launchpad.net/goose/nova"
	"launchpad.net/goose/swift"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/jujutest"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/trivial"
	"net/http"
)

// This provides the content for code accessing test:///... URLs. This allows
// us to set the responses for things like the Metadata server, by pointing
// metadata requests at test:///... rather than http://169.254.169.254
var testRoundTripper = &jujutest.ProxyRoundTripper{}

func init() {
	http.DefaultTransport.(*http.Transport).RegisterProtocol("test", testRoundTripper)
}

var origMetadataHost = metadataHost

var metadataContent = `{"uuid": "d8e02d56-2648-49a3-bf97-6be8f1204f38",` +
	`"availability_zone": "nova", "hostname": "test.novalocal", ` +
	`"launch_index": 0, "meta": {"priority": "low", "role": "webserver"}, ` +
	`"public_keys": {"mykey": "ssh-rsa fake-key\n"}, "name": "test"}`

// A group of canned responses for the "metadata server". These match
// reasonably well with the results of making those requests on a Folsom+
// Openstack service
var MetadataTestingBase = []jujutest.FileContent{
	{"/latest/meta-data/instance-id", "i-000abc"},
	{"/latest/meta-data/local-ipv4", "10.1.1.2"},
	{"/latest/meta-data/public-ipv4", "203.1.1.2"},
	{"/openstack/2012-08-10/meta_data.json", metadataContent},
}

// This is the same as MetadataTestingBase, but it doesn't have the openstack
// 2012-08-08 API. This matches what is available in HP Cloud.
var MetadataHP = MetadataTestingBase[:len(MetadataTestingBase)-1]

// Set Metadata requests to be served by the filecontent supplied.
func UseTestMetadata(metadata []jujutest.FileContent) {
	if len(metadata) != 0 {
		testRoundTripper.Sub = jujutest.NewVirtualRoundTripper(metadata)
		metadataHost = "test:"
	} else {
		testRoundTripper.Sub = nil
		metadataHost = origMetadataHost
	}
}

var originalShortAttempt = shortAttempt
var originalLongAttempt = longAttempt

// ShortTimeouts sets the timeouts to a short period as we
// know that the testing server doesn't get better with time,
// and this reduces the test time from 30s to 3s.
func ShortTimeouts(short bool) {
	if short {
		shortAttempt = trivial.AttemptStrategy{
			Total: 0.10e9,
			Delay: 0.01e9,
		}
		longAttempt = shortAttempt
	} else {
		shortAttempt = originalShortAttempt
		longAttempt = originalLongAttempt
	}
}

var ShortAttempt = &shortAttempt

func DeleteStorageContent(s environs.Storage) error {
	return s.(*storage).deleteAll()
}

// WritablePublicStorage returns a Storage instance which is authorised to write to the PublicStorage bucket.
// It is used by tests which need to upload files.
func WritablePublicStorage(e environs.Environ) environs.Storage {
	ecfg := e.(*environ).ecfg()
	authModeCfg := AuthMode(ecfg.authMode())
	writablePublicStorage := &storage{
		containerName: ecfg.publicBucket(),
		swift:         swift.New(e.(*environ).client(ecfg, authModeCfg)),
	}

	// Ensure the container exists.
	err := writablePublicStorage.makeContainer(ecfg.publicBucket(), swift.PublicRead)
	if err != nil {
		panic(fmt.Errorf("cannot create writable public container: %v", err))
	}
	return writablePublicStorage
}
func InstanceAddress(addresses map[string][]nova.IPAddress) (string, error) {
	return instanceAddress(addresses)
}

func FindInstanceSpec(e environs.Environ, series, arch, flavor string) (imageId, flavorId string, err error) {
	env := e.(*environ)
	spec, err := findInstanceSpec(env, &instanceConstraint{
		series: series,
		arch:   arch,
		region: env.ecfg().region(),
		flavor: flavor,
	})
	if err == nil {
		imageId = spec.imageId
		flavorId = spec.flavorId
	}
	return
}

func SetUseFloatingIP(e environs.Environ, val bool) {
	env := e.(*environ)
	env.ecfg().attrs["use-floating-ip"] = val
}

func DefaultInstanceType(e environs.Environ) string {
	ecfg := e.(*environ).ecfg()
	return ecfg.defaultInstanceType()
}

// ImageDetails specify parameters used to start a test machine for the live tests.
type ImageDetails struct {
	Flavor  string
	ImageId string
}

type BootstrapState struct {
	StateInstances []state.InstanceId
}

func LoadState(e environs.Environ) (*BootstrapState, error) {
	s, err := e.(*environ).loadState()
	if err != nil {
		return nil, err
	}
	return &BootstrapState{s.StateInstances}, nil
}
<commit_msg>Switch to time.Millisecond syntax.<commit_after>package openstack

import (
	"fmt"
	"launchpad.net/goose/nova"
	"launchpad.net/goose/swift"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/jujutest"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/trivial"
	"net/http"
        "time"
)

// This provides the content for code accessing test:///... URLs. This allows
// us to set the responses for things like the Metadata server, by pointing
// metadata requests at test:///... rather than http://169.254.169.254
var testRoundTripper = &jujutest.ProxyRoundTripper{}

func init() {
	http.DefaultTransport.(*http.Transport).RegisterProtocol("test", testRoundTripper)
}

var origMetadataHost = metadataHost

var metadataContent = `{"uuid": "d8e02d56-2648-49a3-bf97-6be8f1204f38",` +
	`"availability_zone": "nova", "hostname": "test.novalocal", ` +
	`"launch_index": 0, "meta": {"priority": "low", "role": "webserver"}, ` +
	`"public_keys": {"mykey": "ssh-rsa fake-key\n"}, "name": "test"}`

// A group of canned responses for the "metadata server". These match
// reasonably well with the results of making those requests on a Folsom+
// Openstack service
var MetadataTestingBase = []jujutest.FileContent{
	{"/latest/meta-data/instance-id", "i-000abc"},
	{"/latest/meta-data/local-ipv4", "10.1.1.2"},
	{"/latest/meta-data/public-ipv4", "203.1.1.2"},
	{"/openstack/2012-08-10/meta_data.json", metadataContent},
}

// This is the same as MetadataTestingBase, but it doesn't have the openstack
// 2012-08-08 API. This matches what is available in HP Cloud.
var MetadataHP = MetadataTestingBase[:len(MetadataTestingBase)-1]

// Set Metadata requests to be served by the filecontent supplied.
func UseTestMetadata(metadata []jujutest.FileContent) {
	if len(metadata) != 0 {
		testRoundTripper.Sub = jujutest.NewVirtualRoundTripper(metadata)
		metadataHost = "test:"
	} else {
		testRoundTripper.Sub = nil
		metadataHost = origMetadataHost
	}
}

var originalShortAttempt = shortAttempt
var originalLongAttempt = longAttempt

// ShortTimeouts sets the timeouts to a short period as we
// know that the testing server doesn't get better with time,
// and this reduces the test time from 30s to 3s.
func ShortTimeouts(short bool) {
	if short {
		shortAttempt = trivial.AttemptStrategy{
			Total: 100 * time.Millisecond,
			Delay: 10 * time.Millisecond,
		}
		longAttempt = shortAttempt
	} else {
		shortAttempt = originalShortAttempt
		longAttempt = originalLongAttempt
	}
}

var ShortAttempt = &shortAttempt

func DeleteStorageContent(s environs.Storage) error {
	return s.(*storage).deleteAll()
}

// WritablePublicStorage returns a Storage instance which is authorised to write to the PublicStorage bucket.
// It is used by tests which need to upload files.
func WritablePublicStorage(e environs.Environ) environs.Storage {
	ecfg := e.(*environ).ecfg()
	authModeCfg := AuthMode(ecfg.authMode())
	writablePublicStorage := &storage{
		containerName: ecfg.publicBucket(),
		swift:         swift.New(e.(*environ).client(ecfg, authModeCfg)),
	}

	// Ensure the container exists.
	err := writablePublicStorage.makeContainer(ecfg.publicBucket(), swift.PublicRead)
	if err != nil {
		panic(fmt.Errorf("cannot create writable public container: %v", err))
	}
	return writablePublicStorage
}
func InstanceAddress(addresses map[string][]nova.IPAddress) (string, error) {
	return instanceAddress(addresses)
}

func FindInstanceSpec(e environs.Environ, series, arch, flavor string) (imageId, flavorId string, err error) {
	env := e.(*environ)
	spec, err := findInstanceSpec(env, &instanceConstraint{
		series: series,
		arch:   arch,
		region: env.ecfg().region(),
		flavor: flavor,
	})
	if err == nil {
		imageId = spec.imageId
		flavorId = spec.flavorId
	}
	return
}

func SetUseFloatingIP(e environs.Environ, val bool) {
	env := e.(*environ)
	env.ecfg().attrs["use-floating-ip"] = val
}

func DefaultInstanceType(e environs.Environ) string {
	ecfg := e.(*environ).ecfg()
	return ecfg.defaultInstanceType()
}

// ImageDetails specify parameters used to start a test machine for the live tests.
type ImageDetails struct {
	Flavor  string
	ImageId string
}

type BootstrapState struct {
	StateInstances []state.InstanceId
}

func LoadState(e environs.Environ) (*BootstrapState, error) {
	s, err := e.(*environ).loadState()
	if err != nil {
		return nil, err
	}
	return &BootstrapState{s.StateInstances}, nil
}
<|endoftext|>


<commit_before>//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    producer.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    02/01/2017
//:
//: 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 producer

import (
	"log"
	"sync"
)

// Producer represents messaging queue
type Producer struct {
	MQ           MQueue
	MQConfigFile string
	MQErrorCount *uint64

	Topic string
	Chan  chan []byte

	Logger *log.Logger
}

// MQueue represents messaging queue methods
type MQueue interface {
	setup(string, *log.Logger) error
	inputMsg(string, chan []byte, *uint64)
}

// NewProducer constructs new Messaging Queue
func NewProducer(mqName string) *Producer {
	//noinspection GoUnresolvedReference,GoInvalidCompositeLiteral
	var mqRegistered = map[string]MQueue{
		"kafka":     new(Kafka),
		"nsq":       new(NSQ),
		"nats":      new(NATS),
		"rawSocket": new(RawSocket),
	}

	return &Producer{
		MQ: mqRegistered[mqName],
	}
}

// Run configs and tries to be ready to produce
func (p *Producer) Run() error {
	var (
		wg  sync.WaitGroup
		err error
	)

	err = p.MQ.setup(p.MQConfigFile, p.Logger)
	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		topic := p.Topic
		p.MQ.inputMsg(topic, p.Chan, p.MQErrorCount)
	}()

	wg.Wait()

	return nil
}

// Shutdown stops the producer
func (p *Producer) Shutdown() {
	close(p.Chan)
}
<commit_msg>Revert "make GoLand happy"<commit_after>//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    producer.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    02/01/2017
//:
//: 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 producer

import (
	"log"
	"sync"
)

// Producer represents messaging queue
type Producer struct {
	MQ           MQueue
	MQConfigFile string
	MQErrorCount *uint64

	Topic string
	Chan  chan []byte

	Logger *log.Logger
}

// MQueue represents messaging queue methods
type MQueue interface {
	setup(string, *log.Logger) error
	inputMsg(string, chan []byte, *uint64)
}

// NewProducer constructs new Messaging Queue
func NewProducer(mqName string) *Producer {
	var mqRegistered = map[string]MQueue{
		"kafka":     new(Kafka),
		"nsq":       new(NSQ),
		"nats":      new(NATS),
		"rawSocket": new(RawSocket),
	}

	return &Producer{
		MQ: mqRegistered[mqName],
	}
}

// Run configs and tries to be ready to produce
func (p *Producer) Run() error {
	var (
		wg  sync.WaitGroup
		err error
	)

	err = p.MQ.setup(p.MQConfigFile, p.Logger)
	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		topic := p.Topic
		p.MQ.inputMsg(topic, p.Chan, p.MQErrorCount)
	}()

	wg.Wait()

	return nil
}

// Shutdown stops the producer
func (p *Producer) Shutdown() {
	close(p.Chan)
}
<|endoftext|>