func (a *Agent) upsertPorterContainer(opts PorterServerStartOpts) (id string, err error) {
	containers, err := a.getContainersCreatedByStart()
	for _, container := range containers {
		if len(container.Names) > 0 && container.Names[0] == "/"+opts.Name {
			timeout, _ := time.ParseDuration("15s")
			err := a.client.ContainerStop(a.ctx, container.ID, &timeout)
			if err != nil {
				return "", a.handleDockerClientErr(err, "Could not stop container "+container.ID)
			}
			err = a.client.ContainerRemove(a.ctx, container.ID, types.ContainerRemoveOptions{})
			if err != nil {
				return "", a.handleDockerClientErr(err, "Could not remove container "+container.ID)
			}
		}
	}
	return a.pullAndCreatePorterContainer(opts)
}
detect if container exists and is running, and stop
if spec has changed, remove and recreate container
if container does not exist, create the container
otherwise, return stopped container
package docker
import (
	"fmt"
	"strings"
	"time"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/go-connections/nat"
	specs "github.com/opencontainers/image-spec/specs-go/v1"
)
type PorterDB int
const (
	Postgres PorterDB = iota
	SQLite
)
type PorterStartOpts struct {
	ProcessID      string
	ServerImageTag string
	ServerPort     int
	DB             PorterDB
	Env            []string
}
func StartPorter(opts *PorterStartOpts) (agent *Agent, id string, err error) {
	agent, err = NewAgentFromEnv()
	if err != nil {
		return nil, "", err
	}
	mounts := make([]mount.Mount, 0)
	volumesMap := make(map[string]struct{})
	netID, err := agent.CreateBridgeNetworkIfNotExist("porter_network_" + opts.ProcessID)
	if err != nil {
		return nil, "", err
	}
	switch opts.DB {
	case SQLite:
		vol, err := agent.CreateLocalVolumeIfNotExist("porter_sqlite_" + opts.ProcessID)
		if err != nil {
			return nil, "", err
		}
		mount := mount.Mount{
			Type:        mount.TypeVolume,
			Source:      vol.Name,
			Target:      "/sqlite",
			ReadOnly:    false,
			Consistency: mount.ConsistencyFull,
		}
		mounts = append(mounts, mount)
		volumesMap[vol.Name] = struct{}{}
		opts.Env = append(opts.Env, []string{
			"SQL_LITE=true",
			"SQL_LITE_PATH=/sqlite/porter.db",
		}...)
	case Postgres:
		vol, err := agent.CreateLocalVolumeIfNotExist("porter_postgres_" + opts.ProcessID)
		if err != nil {
			return nil, "", err
		}
		pgMount := []mount.Mount{
			{
				Type:        mount.TypeVolume,
				Source:      vol.Name,
				Target:      "/var/lib/postgresql/data",
				ReadOnly:    false,
				Consistency: mount.ConsistencyFull,
			},
		}
		startOpts := PostgresOpts{
			Name:   "porter_postgres_" + opts.ProcessID,
			Image:  "postgres:latest",
			Mounts: pgMount,
			VolumeMap: map[string]struct{}{
				"porter_postgres": {},
			},
			NetworkID: netID,
			Env: []string{
				"POSTGRES_USER=porter",
				"POSTGRES_PASSWORD=porter",
				"POSTGRES_DB=porter",
			},
		}
		pgID, err := agent.StartPostgresContainer(startOpts)
		if err != nil {
			return nil, "", err
		}
		err = agent.WaitForContainerHealthy(pgID, 10)
		if err != nil {
			return nil, "", err
		}
		opts.Env = append(opts.Env, []string{
			"SQL_LITE=false",
			"DB_USER=porter",
			"DB_PASS=porter",
			"DB_NAME=porter",
			"DB_HOST=porter_postgres_" + opts.ProcessID,
			"DB_PORT=5432",
		}...)
	}
	opts.Env = append(opts.Env, "REDIS_ENABLED=false")
	startOpts := PorterServerStartOpts{
		Name:          "porter_server_" + opts.ProcessID,
		Image:         "porter1/porter:" + opts.ServerImageTag,
		HostPort:      uint(opts.ServerPort),
		ContainerPort: 8080,
		Mounts:        mounts,
		VolumeMap:     volumesMap,
		NetworkID:     netID,
		Env:           opts.Env,
	}
	id, err = agent.StartPorterContainer(startOpts)
	if err != nil {
		return nil, "", err
	}
	err = agent.WaitForContainerHealthy(id, 10)
	if err != nil {
		return nil, "", err
	}
	return agent, id, nil
}
type PorterServerStartOpts struct {
	Name          string
	Image         string
	StartCmd      []string
	HostPort      uint
	ContainerPort uint
	Mounts        []mount.Mount
	VolumeMap     map[string]struct{}
	Env           []string
	NetworkID     string
}
func (a *Agent) StartPorterContainer(opts PorterServerStartOpts) (string, error) {
	id, err := a.upsertPorterContainer(opts)
	if err != nil {
		return "", err
	}
	err = a.startPorterContainer(id)
	if err != nil {
		return "", err
	}
	err = a.ConnectContainerToNetwork(opts.NetworkID, id, opts.Name)
	if err != nil {
		return "", err
	}
	return id, nil
}