<commit_before>package sobjects

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"regexp"
	"strings"

	"github.com/grokify/gotilla/io/ioutilmore"
	"github.com/grokify/gotilla/net/httputilmore"
)

type ContactSet struct {
	IdSet      IdSet              `xml:"-"`
	Records    []Contact          `json:"records,omitempty" xml:"records"`
	RecordsMap map[string]Contact `xml:"-"`
}

func NewContactSet() ContactSet {
	set := ContactSet{
		IdSet:      NewIdSet(),
		Records:    []Contact{},
		RecordsMap: map[string]Contact{}}
	return set
}

func NewContactSetSetFromXml(bytes []byte) (ContactSet, error) {
	set := ContactSet{IdSet: NewIdSet()}
	err := xml.Unmarshal(bytes, &set)
	set.Inflate()
	return set, err
}

func NewContactSetFromXmlFile(filepath string) (ContactSet, error) {
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return ContactSet{}, err
	}
	return NewContactSetSetFromXml(bytes)
}

func NewContactSetFromJSONResponse(resp *http.Response) (ContactSet, error) {
	set := NewContactSet()
	bytes, err := httputilmore.ResponseBody(resp)
	if err != nil {
		return set, err
	}
	err = json.Unmarshal(bytes, &set)
	return set, err
}

func (set *ContactSet) ReadJsonFilesFromDir(dir string) error {
	files, err := ioutilmore.DirEntriesReSizeGt0(dir, regexp.MustCompile(`(?i)\.json$`))
	if err != nil {
		return err
	}
	for _, fi := range files {
		filepath := path.Join(dir, fi.Name())
		contact, err := NewContactFromJsonFile(filepath)
		if err == nil && len(contact.Id) > 0 {
			set.Records = append(set.Records, contact)
		}
	}
	return nil
}

func (set *ContactSet) Inflate() {
	for _, record := range set.Records {
		if len(record.Id) > 0 {
			set.IdSet.AddId(record.Id)
			set.RecordsMap[record.Id] = record
		}
		if len(record.AccountId) > 0 {
			set.IdSet.AddId(record.AccountId)
		}
	}
}

func (set *ContactSet) GetContactByName(name string) (Contact, error) {
	for _, contact := range set.Records {
		if contact.Name == name {
			return contact, nil
		}
	}
	return Contact{}, errors.New(fmt.Sprintf("Could not found Contact by name [%v]", name))
}

func (set *ContactSet) GetContactById(id string) (Contact, error) {
	for _, contact := range set.Records {
		if contact.Id == id {
			return contact, nil
		}
	}
	return Contact{}, errors.New(fmt.Sprintf("Could not found Contact by id [%v]", id))
}

type Contact struct {
	Id         string
	AccountId  string
	Department string
	Email      string
	Fax        string
	FirstName  string
	LastName   string
	Name       string
}

func NewContactFromJson(bytes []byte) (Contact, error) {
	obj := Contact{}
	err := json.Unmarshal(bytes, &obj)
	return obj, err
}

func NewContactFromJsonFile(filepath string) (Contact, error) {
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return Contact{}, err
	}
	return NewContactFromJson(bytes)
}

func ContactEmailOrId(contact Contact) string {
	emailOrId := ""
	if len(strings.TrimSpace(contact.Email)) > 0 {
		emailOrId = contact.Email
	} else {
		emailOrId = contact.Id
	}
	return strings.TrimSpace(emailOrId)
}

func ContactsEmailOrId(contacts []Contact) []string {
	emailOrIds := []string{}
	for _, contact := range contacts {
		emailOrId := ContactEmailOrId(contact)
		if len(emailOrId) > 0 {
			emailOrIds = append(emailOrIds, emailOrId)
		}
	}
	return emailOrIds
}

func ContactsEmailOrIdString(contacts []Contact, sep string) string {
	return strings.Join(ContactsEmailOrId(contacts), sep)
}

func ContactIdOrEmail(contact Contact) string {
	idOrEmail := ""
	if len(strings.TrimSpace(contact.Id)) > 0 {
		idOrEmail = contact.Id
	} else {
		idOrEmail = contact.Email
	}
	return strings.TrimSpace(idOrEmail)
}

func ContactsIdOrEmail(contacts []Contact) []string {
	idOrEmails := []string{}
	for _, contact := range contacts {
		idOrEmail := ContactIdOrEmail(contact)
		if len(idOrEmail) > 0 {
			idOrEmails = append(idOrEmails, idOrEmail)
		}
	}
	return idOrEmails
}

func ContactsIdOrEmailString(contacts []Contact, sep string) string {
	return strings.Join(ContactsIdOrEmail(contacts), sep)
}
<commit_msg>update for dependency<commit_after>package sobjects

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"regexp"
	"strings"

	"github.com/grokify/gotilla/io/ioutilmore"
	"github.com/grokify/gotilla/net/httputilmore"
)

type ContactSet struct {
	IdSet      IdSet              `xml:"-"`
	Records    []Contact          `json:"records,omitempty" xml:"records"`
	RecordsMap map[string]Contact `xml:"-"`
}

func NewContactSet() ContactSet {
	set := ContactSet{
		IdSet:      NewIdSet(),
		Records:    []Contact{},
		RecordsMap: map[string]Contact{}}
	return set
}

func NewContactSetSetFromXml(bytes []byte) (ContactSet, error) {
	set := ContactSet{IdSet: NewIdSet()}
	err := xml.Unmarshal(bytes, &set)
	set.Inflate()
	return set, err
}

func NewContactSetFromXmlFile(filepath string) (ContactSet, error) {
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return ContactSet{}, err
	}
	return NewContactSetSetFromXml(bytes)
}

func NewContactSetFromJSONResponse(resp *http.Response) (ContactSet, error) {
	set := NewContactSet()
	bytes, err := httputilmore.ResponseBody(resp)
	if err != nil {
		return set, err
	}
	err = json.Unmarshal(bytes, &set)
	return set, err
}

func (set *ContactSet) ReadJsonFilesFromDir(dir string) error {
	files, err := ioutilmore.DirEntriesReNotEmpty(dir, regexp.MustCompile(`(?i)\.json$`))
	if err != nil {
		return err
	}
	for _, fi := range files {
		filepath := path.Join(dir, fi.Name())
		contact, err := NewContactFromJsonFile(filepath)
		if err == nil && len(contact.Id) > 0 {
			set.Records = append(set.Records, contact)
		}
	}
	return nil
}

func (set *ContactSet) Inflate() {
	for _, record := range set.Records {
		if len(record.Id) > 0 {
			set.IdSet.AddId(record.Id)
			set.RecordsMap[record.Id] = record
		}
		if len(record.AccountId) > 0 {
			set.IdSet.AddId(record.AccountId)
		}
	}
}

func (set *ContactSet) GetContactByName(name string) (Contact, error) {
	for _, contact := range set.Records {
		if contact.Name == name {
			return contact, nil
		}
	}
	return Contact{}, errors.New(fmt.Sprintf("Could not found Contact by name [%v]", name))
}

func (set *ContactSet) GetContactById(id string) (Contact, error) {
	for _, contact := range set.Records {
		if contact.Id == id {
			return contact, nil
		}
	}
	return Contact{}, errors.New(fmt.Sprintf("Could not found Contact by id [%v]", id))
}

type Contact struct {
	Id         string
	AccountId  string
	Department string
	Email      string
	Fax        string
	FirstName  string
	LastName   string
	Name       string
}

func NewContactFromJson(bytes []byte) (Contact, error) {
	obj := Contact{}
	err := json.Unmarshal(bytes, &obj)
	return obj, err
}

func NewContactFromJsonFile(filepath string) (Contact, error) {
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return Contact{}, err
	}
	return NewContactFromJson(bytes)
}

func ContactEmailOrId(contact Contact) string {
	emailOrId := ""
	if len(strings.TrimSpace(contact.Email)) > 0 {
		emailOrId = contact.Email
	} else {
		emailOrId = contact.Id
	}
	return strings.TrimSpace(emailOrId)
}

func ContactsEmailOrId(contacts []Contact) []string {
	emailOrIds := []string{}
	for _, contact := range contacts {
		emailOrId := ContactEmailOrId(contact)
		if len(emailOrId) > 0 {
			emailOrIds = append(emailOrIds, emailOrId)
		}
	}
	return emailOrIds
}

func ContactsEmailOrIdString(contacts []Contact, sep string) string {
	return strings.Join(ContactsEmailOrId(contacts), sep)
}

func ContactIdOrEmail(contact Contact) string {
	idOrEmail := ""
	if len(strings.TrimSpace(contact.Id)) > 0 {
		idOrEmail = contact.Id
	} else {
		idOrEmail = contact.Email
	}
	return strings.TrimSpace(idOrEmail)
}

func ContactsIdOrEmail(contacts []Contact) []string {
	idOrEmails := []string{}
	for _, contact := range contacts {
		idOrEmail := ContactIdOrEmail(contact)
		if len(idOrEmail) > 0 {
			idOrEmails = append(idOrEmails, idOrEmail)
		}
	}
	return idOrEmails
}

func ContactsIdOrEmailString(contacts []Contact, sep string) string {
	return strings.Join(ContactsIdOrEmail(contacts), sep)
}
<|endoftext|>


<commit_before>package cmd

// Version defines the current Pop version.
const Version = "development"
<commit_msg>Bump version<commit_after>package cmd

// Version defines the current Pop version.
const Version = "v4.11.0"
<|endoftext|>


<commit_before>package flame

import (
	"net/http"

	"github.com/256dpi/fire"
	"github.com/256dpi/fire/coal"

	"golang.org/x/crypto/bcrypt"
)

var tester = fire.NewTester(coal.MustCreateStore("mongodb://0.0.0.0:27017/test-flame"), &User{}, &Application{}, &AccessToken{}, &RefreshToken{})

func newHandler(auth *Authenticator, force bool) http.Handler {
	router := http.NewServeMux()

	router.Handle("/oauth2/", auth.Endpoint("/oauth2/"))

	authorizer := auth.Authorizer("foo", force)

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})

	router.Handle("/api/protected", authorizer(handler))

	return router
}

func mustHash(password string) []byte {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost)
	if err != nil {
		panic(err)
	}

	return hash
}
<commit_msg>simplified<commit_after>package flame

import (
	"net/http"

	"github.com/256dpi/fire"
	"github.com/256dpi/fire/coal"

	"golang.org/x/crypto/bcrypt"
)

var tester = fire.NewTester(coal.MustCreateStore("mongodb://0.0.0.0:27017/test-flame"), &User{}, &Application{}, &AccessToken{}, &RefreshToken{})

func newHandler(auth *Authenticator, force bool) http.Handler {
	router := http.NewServeMux()
	router.Handle("/oauth2/", auth.Endpoint("/oauth2/"))

	authorizer := auth.Authorizer("foo", force)
	router.Handle("/api/protected", authorizer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})))

	return router
}

func mustHash(password string) []byte {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost)
	if err != nil {
		panic(err)
	}

	return hash
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "fmt"
    "time"
    "math/rand"
    "net"
    "net/rpc"
)

type Heartbeat struct {
    LeaderID string
    Term int
}

type HeartbeatResponse struct {
    Success bool
    Term int
}

type VoteRequest struct {
    CandidateID string
    Term int
}

type VoteResponse struct {
    VoteGranted bool
    Term int
}

var leaderMsg chan Heartbeat
var candidateMsg chan VoteRequest
var voterMsg chan VoteResponse

type Message int

//
func (t *Message) AppendEntries(heartbeat Heartbeat, heartbeatResponse *HeartbeatResponse) error {
    leaderMsg <- heartbeat
    return nil
}

//
func (s *Message) RequestVote(voteRequest VoteRequest, voteResponse *VoteResponse) error {
    candidateMsg <- voteRequest
    *voteResponse = <-voterMsg
    return nil
}

func main() {

    // validate arguments or print usage
    if len(os.Args) < 2 {
        fmt.Println("usage:", os.Args[0], "thisAddress [thatAddress]...")
        os.Exit(1)
    }

    // process id
    pid := os.Getpid()

    // state
    state := "follower"
    fmt.Println(pid, "INITIAL STATE", state)

    // term number
    term := 0

    // address of this server
    thisAddress := os.Args[1]
    fmt.Println(pid, "LISTEN", thisAddress)

    // addresses of other servers
    thatAddress := os.Args[2:]
    for _,address := range thatAddress {
        fmt.Println(pid, "PEER", address)
    }

    // address of leader
//    leadAddress := ""

    // cluster size
    clusterSize := len(os.Args[1:])
    fmt.Println(pid, "CLUSTER SIZE", clusterSize)

    // votes
    votes := 0

    // election timeout between 1500 and 3000ms
    rand.Seed(int64(pid))
    number :=  1500 + rand.Intn(1500)
    electionTimeout := time.Millisecond * time.Duration(number)
    fmt.Println(pid, "RANDOM TIMEOUT", electionTimeout)

    // heartbeat timeout
    heartbeatTimeout := time.Millisecond * time.Duration(1000)

    // vote timeout
    voteTimeout := time.Millisecond * time.Duration(1000)

    //
    leaderMsg = make(chan Heartbeat)
    candidateMsg = make(chan VoteRequest)
    voterMsg = make(chan VoteResponse)

    //
    rpc.Register(new(Message))

    //
    messages, error := net.Listen("tcp", thisAddress)
    if error != nil {
        fmt.Println(pid, "UNABLE TO LISTEN ON", thisAddress)
        os.Exit(1)
    }
    go rpc.Accept(messages)

    // event loop
    for {

        switch state {

        case "follower":

            select {

            // receive leader message before timeout
            case <-leaderMsg:
                fmt.Println(pid, "LEADER MESSAGE RECEIVED")
//                followerMsg <- HeartbeatResponse{Success: true, Term: term}

            // receive vote request
            case <-candidateMsg:
                fmt.Println(pid, "CANDIDATE MESSAGE RECEIVED")
                voterMsg <- VoteResponse{VoteGranted: true, Term: term}

            // otherwise begin election
            case <-time.After(electionTimeout):
                state = "candidate"
                fmt.Println(pid, "ELECTION TIMEOUT")
                fmt.Println(pid, "STATE", state)
            }

        case "candidate":

            // increment term
            term++
            fmt.Println(pid, "TERM", term)

            // vote for self
            votes = 1

            // request votes
            for _,address := range thatAddress {
                go func(){
                    client, error := rpc.Dial("tcp", address)
                    if error != nil {
                        fmt.Println(pid, "UNABLE TO DIAL", address)
                    } else {
                        fmt.Println(pid, "REQUEST VOTE FROM", address)
                    }
                    vreq := new(VoteRequest)
                    vreq.CandidateID = thisAddress
                    vreq.Term = term
                    vresp := new(VoteResponse)
                    client.Call("Message.RequestVote", vreq, &vresp)
                    voterMsg <- *vresp
                }()
            }

            election: for {
                select {

                // receive votes
                case <-voterMsg:
                    fmt.Println(pid, "VOTE RECEIVED")
                    votes++

                    // if majority of votes, go to leader state
                    if votes > clusterSize/2 {
                        state = "leader"
                        fmt.Println(pid, "STATE", state)
                        break election
                    }

                // receive leader challenge
                case <-leaderMsg:
                    fmt.Println(pid, "LEADER CHALLENGE RECEIVED")

                    // if that term >= this term, return to follower state
                    // TODO
                    if true {
                        state = "follower"
                        fmt.Println(pid, "STATE", state)
                        break election
                    }

                // time out and start new election
                case <-time.After(voteTimeout):
                    fmt.Println(pid, "VOTE TIMEOUT")
                    break election
                }
            }

        case "leader":

            // send heartbeat
            for _,address := range thatAddress {
                go func(){
                    client, error := rpc.Dial("tcp", address)
                    if error != nil {
                        fmt.Println(pid, "UNABLE TO DIAL", address)
                    } else {
                        fmt.Println(pid, "SEND HEARTBEAT TO", address)
                    }
                    hb := new(Heartbeat)
                    hb.LeaderID = thisAddress
                    hb.Term = term
                    hbr := new(HeartbeatResponse)
                    client.Call("Message.AppendEntries", hb, &hbr)
                    fmt.Println(pid, "RECEIVE HEARTBEAT RESPONSE FROM ", address)
                }()
            }

            // wait
            time.Sleep(heartbeatTimeout)

        }
    }
}
<commit_msg>pass address as argument to goroutine<commit_after>package main

import (
    "os"
    "fmt"
    "time"
    "math/rand"
    "net"
    "net/rpc"
)

type Heartbeat struct {
    LeaderID string
    Term int
}

type HeartbeatResponse struct {
    Success bool
    Term int
}

type VoteRequest struct {
    CandidateID string
    Term int
}

type VoteResponse struct {
    VoteGranted bool
    Term int
}

var leaderMsg chan Heartbeat
var candidateMsg chan VoteRequest
var voterMsg chan VoteResponse

type Message int

//
func (t *Message) AppendEntries(heartbeat Heartbeat, heartbeatResponse *HeartbeatResponse) error {
    leaderMsg <- heartbeat
    return nil
}

//
func (s *Message) RequestVote(voteRequest VoteRequest, voteResponse *VoteResponse) error {
    candidateMsg <- voteRequest
    *voteResponse = <-voterMsg
    return nil
}

func main() {

    // validate arguments or print usage
    if len(os.Args) < 2 {
        fmt.Println("usage:", os.Args[0], "thisAddress [thatAddress]...")
        os.Exit(1)
    }

    // process id
    pid := os.Getpid()

    // state
    state := "follower"
    fmt.Println(pid, "INITIAL STATE", state)

    // term number
    term := 0

    // address of this server
    thisAddress := os.Args[1]
    fmt.Println(pid, "LISTEN", thisAddress)

    // addresses of other servers
    thatAddress := os.Args[2:]
    for _,address := range thatAddress {
        fmt.Println(pid, "PEER", address)
    }

    // address of leader
//    leadAddress := ""

    // cluster size
    clusterSize := len(os.Args[1:])
    fmt.Println(pid, "CLUSTER SIZE", clusterSize)

    // votes
    votes := 0

    // election timeout between 1500 and 3000ms
    rand.Seed(int64(pid))
    number :=  1500 + rand.Intn(1500)
    electionTimeout := time.Millisecond * time.Duration(number)
    fmt.Println(pid, "RANDOM TIMEOUT", electionTimeout)

    // heartbeat timeout
    heartbeatTimeout := time.Millisecond * time.Duration(1000)

    // vote timeout
    voteTimeout := time.Millisecond * time.Duration(1000)

    //
    leaderMsg = make(chan Heartbeat)
    candidateMsg = make(chan VoteRequest)
    voterMsg = make(chan VoteResponse)

    //
    rpc.Register(new(Message))

    //
    messages, error := net.Listen("tcp", thisAddress)
    if error != nil {
        fmt.Println(pid, "UNABLE TO LISTEN ON", thisAddress)
        os.Exit(1)
    }
    go rpc.Accept(messages)

    // event loop
    for {

        switch state {

        case "follower":

            select {

            // receive leader message before timeout
            case <-leaderMsg:
                fmt.Println(pid, "LEADER MESSAGE RECEIVED")
//                followerMsg <- HeartbeatResponse{Success: true, Term: term}

            // receive vote request
            case <-candidateMsg:
                fmt.Println(pid, "CANDIDATE MESSAGE RECEIVED")
                voterMsg <- VoteResponse{VoteGranted: true, Term: term}

            // otherwise begin election
            case <-time.After(electionTimeout):
                state = "candidate"
                fmt.Println(pid, "ELECTION TIMEOUT")
                fmt.Println(pid, "STATE", state)
            }

        case "candidate":

            // increment term
            term++
            fmt.Println(pid, "TERM", term)

            // vote for self
            votes = 1

            // request votes
            for _,address := range thatAddress {
                go func(address string){
                    client, error := rpc.Dial("tcp", address)
                    if error != nil {
                        fmt.Println(pid, "UNABLE TO DIAL", address)
                    } else {
                        fmt.Println(pid, "REQUEST VOTE FROM", address)
                    }
                    vreq := new(VoteRequest)
                    vreq.CandidateID = thisAddress
                    vreq.Term = term
                    vresp := new(VoteResponse)
                    client.Call("Message.RequestVote", vreq, &vresp)
                    voterMsg <- *vresp
                }(address)
            }

            election: for {
                select {

                // receive votes
                case <-voterMsg:
                    fmt.Println(pid, "VOTE RECEIVED")
                    votes++

                    // if majority of votes, go to leader state
                    if votes > clusterSize/2 {
                        state = "leader"
                        fmt.Println(pid, "STATE", state)
                        break election
                    }

                // receive leader challenge
                case <-leaderMsg:
                    fmt.Println(pid, "LEADER CHALLENGE RECEIVED")

                    // if that term >= this term, return to follower state
                    // TODO
                    if true {
                        state = "follower"
                        fmt.Println(pid, "STATE", state)
                        break election
                    }

                // time out and start new election
                case <-time.After(voteTimeout):
                    fmt.Println(pid, "VOTE TIMEOUT")
                    break election
                }
            }

        case "leader":

            // send heartbeat
            for _,address := range thatAddress {
                go func(address string){
                    client, error := rpc.Dial("tcp", address)
                    if error != nil {
                        fmt.Println(pid, "UNABLE TO DIAL", address)
                    } else {
                        fmt.Println(pid, "SEND HEARTBEAT TO", address)
                    }
                    hb := new(Heartbeat)
                    hb.LeaderID = thisAddress
                    hb.Term = term
                    hbr := new(HeartbeatResponse)
                    client.Call("Message.AppendEntries", hb, &hbr)
                    fmt.Println(pid, "RECEIVE HEARTBEAT RESPONSE FROM ", address)
                }(address)
            }

            // wait
            time.Sleep(heartbeatTimeout)

        }
    }
}
<|endoftext|>


<commit_before>package hammy

import (
	"fmt"
	"io"
	"time"
	"os"
	"os/exec"
	"bytes"
	"syscall"
	"log"
	"github.com/ugorji/go-msgpack"
)

type process struct {
	*exec.Cmd
	Count uint
	Stdin io.Writer
	Stdout io.Reader
	Stderr bytes.Buffer
}

type WorkerProcessInput struct {
	Key string
	Trigger string
	State *State
	IData IncomingHostData
}

type WorkerProcessOutput struct {
	CmdBuffer *CmdBuffer
	State *State
}

// Executer implementation for subprocesses with MessagePack-based RPC
type SPExecuter struct {
	CmdLine string
	MaxIter uint
	Workers chan *process
	Timeout time.Duration
}

// Create new instance of SPExecutor
// per process
func NewSPExecuter(cfg Config) *SPExecuter {
	if cfg.Workers.PoolSize < 1 || cfg.Workers.CmdLine == "" {
		panic("Invalid argument")
	}

	e := new(SPExecuter)
	e.CmdLine = cfg.Workers.CmdLine
	e.MaxIter = cfg.Workers.MaxIter
	e.Workers = make(chan *process, cfg.Workers.PoolSize)
	e.Timeout = time.Duration(cfg.Workers.Timeout) * time.Second

	for i := uint(0); i < cfg.Workers.PoolSize; i++ {
		e.Workers <- &process{}
	}

	return e
}

func (e *SPExecuter) ProcessTrigger(key string, trigger string, state *State,
		data IncomingHostData) (newState *State, cmdb *CmdBuffer, err error) {
//
	cmdb = NewCmdBuffer(0)
	newState = NewState()
	res := WorkerProcessOutput{
		CmdBuffer: cmdb,
		State: newState,
	}

	// Fetch worker (may be wait for free worker)
	worker, err := e.getWorker()
	if err != nil {
		return
	}
	defer e.freeWorker(worker)

	// Set up timeout
	cTimedOut := make(chan bool)
	cEnd := make(chan int)
	go e.workerTimeout(worker, cEnd, cTimedOut)

	// marshal and send args
	pInput := WorkerProcessInput{
		Key: key,
		Trigger: trigger,
		State: state,
		IData: data,
	}

	enc := msgpack.NewEncoder(worker.Stdin)
	err = enc.Encode(pInput)
	if err != nil {
		return
	}

	// wait, read and unmarshal result
	dec := msgpack.NewDecoder(worker.Stdout, nil)
	err = dec.Decode(&res)
	close(cEnd)
	if err != nil {
		timedOut := <- cTimedOut
		if timedOut {
			err = fmt.Errorf("SPExexuter timeout for host %v", key)
		} else {
			err = fmt.Errorf("SPExexuter error: %#v, child stderr: %#v", err, worker.Stderr.String())
		}
	}
	return
}

// timeout task
func (e *SPExecuter) workerTimeout(worker *process, cEnd chan int, cRes chan bool) {
	select {
	case <-cEnd:
		cRes <- false
	case <-time.After(e.Timeout):
		err := e.workerKill(worker)
		if err != nil {
			log.Printf("%s", err)
		}
		cRes <- true
	}
}

func (e *SPExecuter) workerKill(worker *process) error {
	defer func() {
		worker.Cmd = nil
	}()

	if worker.Cmd == nil || worker.Cmd.Process == nil {
		return nil
	}

	err := worker.Process.Kill()
	switch err {
		case nil:
			//
		case syscall.ECHILD:
			return nil
		default:
			if e, ok := err.(*os.SyscallError); ok && e.Err == syscall.ECHILD {
				return nil
			}
			return fmt.Errorf("SPExecuter: Process.Kill error: %#v", err)
	}

	// Zombies is not good for us...
	_, err = worker.Process.Wait()
	switch err {
		case nil:
			//
		case syscall.ECHILD:
			return nil
		default:
			if e, ok := err.(*os.SyscallError); ok && e.Err == syscall.ECHILD {
				return nil
			}
			return fmt.Errorf("SPExecuter: Process.Wait error: %#v", err)
	}

	return nil
}

// Fetch worker (may be wait for free worker)
func (e *SPExecuter) getWorker() (worker *process, err error) {
	worker = <- e.Workers

	if worker == nil {
		panic("nil worker")
	}

	if worker.Cmd != nil {
		// Check process state
		var status syscall.WaitStatus

		// We can't use worker.ProcessState (it's available only after a call to Wait or Run)
		wpid, err := syscall.Wait4(worker.Process.Pid, &status, syscall.WNOHANG, nil)

		switch {
			case err == nil && wpid == 0:
				// Do nothing
			case err == nil && status.Exited():
				worker.Cmd = nil
			case err != nil:
				if err2, ok := err.(*os.SyscallError); ok && err2.Err == syscall.ECHILD {
					worker.Cmd = nil
				} else {
					log.Printf("SPExecuter: syscall.Wait4 error: %#v", err)
					err = e.workerKill(worker)
					if err != nil {
						log.Printf("%s", err)
					}
				}
			default:
				// Do nothing
		}
	}

	if worker.Cmd == nil {
		// Creating new subprocess
		worker.Count = 0
		worker.Cmd = exec.Command(e.CmdLine)
		worker.Stdin, err = worker.Cmd.StdinPipe()
		if err != nil {
			return
		}
		worker.Stdout, err = worker.Cmd.StdoutPipe()
		if err != nil {
			return
		}
		worker.Cmd.Stderr = &worker.Stderr
		err = worker.Start()
		if err != nil {
			return
		}
	}

	return
}

// Return worker to buffer
func (e *SPExecuter) freeWorker(worker *process) {
	// Increment count of execution for the worker
	worker.Count++

	// Check iteration count
	if worker.Count >= e.MaxIter {
		err := e.workerKill(worker)
		if err != nil {
			log.Printf("%s", err)
		}
	}

	// Return worker to the queue
	e.Workers <- worker
}
<commit_msg>spexecuter fixes<commit_after>package hammy

import (
	"fmt"
	"io"
	"time"
	"os"
	"os/exec"
	"bytes"
	"syscall"
	"log"
	"github.com/ugorji/go-msgpack"
)

type process struct {
	*exec.Cmd
	Count uint
	Stdin io.Writer
	Stdout io.Reader
	Stderr bytes.Buffer
}

type WorkerProcessInput struct {
	Key string
	Trigger string
	State *State
	IData IncomingHostData
}

type WorkerProcessOutput struct {
	CmdBuffer *CmdBuffer
	State *State
}

// Executer implementation for subprocesses with MessagePack-based RPC
type SPExecuter struct {
	CmdLine string
	MaxIter uint
	Workers chan *process
	Timeout time.Duration
}

// Create new instance of SPExecutor
// per process
func NewSPExecuter(cfg Config) *SPExecuter {
	if cfg.Workers.PoolSize < 1 || cfg.Workers.CmdLine == "" {
		panic("Invalid argument")
	}

	e := new(SPExecuter)
	e.CmdLine = cfg.Workers.CmdLine
	e.MaxIter = cfg.Workers.MaxIter
	e.Workers = make(chan *process, cfg.Workers.PoolSize)
	e.Timeout = time.Duration(cfg.Workers.Timeout) * time.Second

	for i := uint(0); i < cfg.Workers.PoolSize; i++ {
		e.Workers <- &process{}
	}

	return e
}

func (e *SPExecuter) ProcessTrigger(key string, trigger string, state *State,
		data IncomingHostData) (newState *State, cmdb *CmdBuffer, err error) {
//
	cmdb = NewCmdBuffer(0)
	newState = NewState()
	res := WorkerProcessOutput{
		CmdBuffer: cmdb,
		State: newState,
	}

	// Fetch worker (may be wait for free worker)
	worker, err := e.getWorker()
	if err != nil {
		return
	}
	defer e.freeWorker(worker)

	// Set up timeout
	cTimedOut := make(chan bool)
	cEnd := make(chan int)
	go e.workerTimeout(worker, cEnd, cTimedOut)

	// marshal and send args
	pInput := WorkerProcessInput{
		Key: key,
		Trigger: trigger,
		State: state,
		IData: data,
	}

	enc := msgpack.NewEncoder(worker.Stdin)
	err = enc.Encode(pInput)
	if err != nil {
		return
	}

	// wait, read and unmarshal result
	dec := msgpack.NewDecoder(worker.Stdout, nil)
	err = dec.Decode(&res)
	close(cEnd)
	if err != nil {
		timedOut := <- cTimedOut
		if timedOut {
			err = fmt.Errorf("SPExexuter timeout for host %v", key)
		} else {
			err = fmt.Errorf("SPExexuter error: %#v, child stderr: %#v", err, worker.Stderr.String())
		}
	}
	return
}

// timeout task
func (e *SPExecuter) workerTimeout(worker *process, cEnd chan int, cRes chan bool) {
	select {
	case <-cEnd:
		cRes <- false
	case <-time.After(e.Timeout):
		err := e.workerKill(worker)
		if err != nil {
			log.Printf("%s", err)
		}
		cRes <- true
	}
}

func (e *SPExecuter) workerKill(worker *process) error {
	defer func() {
		worker.Cmd = nil
	}()

	if worker.Cmd == nil || worker.Cmd.Process == nil {
		return nil
	}

	err := worker.Process.Kill()
	switch err {
		case nil:
			//
		case syscall.ECHILD:
			return nil
		default:
			if e, ok := err.(*os.SyscallError); ok && e.Err == syscall.ECHILD {
				return nil
			}
			return fmt.Errorf("SPExecuter: Process.Kill error: %#v", err)
	}

	// Zombies is not good for us...
	_, err = worker.Process.Wait()
	switch err {
		case nil:
			//
		case syscall.ECHILD:
			return nil
		default:
			if e, ok := err.(*os.SyscallError); ok && e.Err == syscall.ECHILD {
				return nil
			}
			return fmt.Errorf("SPExecuter: Process.Wait error: %#v", err)
	}

	return nil
}

// Fetch worker (may be wait for free worker)
func (e *SPExecuter) getWorker() (worker *process, err error) {
	worker = <- e.Workers

	if worker == nil {
		panic("nil worker")
	}

	if worker.Cmd != nil {
		// Check process state
		var status syscall.WaitStatus

		// We can't use worker.ProcessState (it's available only after a call to Wait or Run)
		wpid, err := syscall.Wait4(worker.Process.Pid, &status, syscall.WNOHANG, nil)

		switch {
			case err == nil && wpid == 0:
				// Do nothing
			case err == nil && status.Exited() || err == syscall.ECHILD:
				worker.Cmd = nil
			case err != nil:
				if err2, ok := err.(*os.SyscallError); ok && err2.Err == syscall.ECHILD {
					worker.Cmd = nil
				} else {
					log.Printf("SPExecuter: syscall.Wait4 error: %#v", err)
					err = e.workerKill(worker)
					if err != nil {
						log.Printf("%s", err)
					}
				}
			default:
				// Do nothing
		}
	}

	if worker.Cmd == nil {
		// Creating new subprocess
		worker.Count = 0
		worker.Cmd = exec.Command(e.CmdLine)
		worker.Stdin, err = worker.Cmd.StdinPipe()
		if err != nil {
			return
		}
		worker.Stdout, err = worker.Cmd.StdoutPipe()
		if err != nil {
			return
		}
		worker.Cmd.Stderr = &worker.Stderr
		err = worker.Start()
		if err != nil {
			return
		}
	}

	return
}

// Return worker to buffer
func (e *SPExecuter) freeWorker(worker *process) {
	// Increment count of execution for the worker
	worker.Count++

	// Check iteration count
	if worker.Count >= e.MaxIter {
		err := e.workerKill(worker)
		if err != nil {
			log.Printf("%s", err)
		}
	}

	// Return worker to the queue
	e.Workers <- worker
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package site

import (
	"github.com/bboozzoo/q3stats/models"
	"log"
	"net/http"
)

type matchesViewMatchData struct {
	models.Match
}

func (s *Site) matchesViewHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("site matches view handler")

	matches := s.m.ListMatches()

	data := struct {
		Matches []matchesViewMatchData
	}{
		make([]matchesViewMatchData, len(matches)),
	}
	for i, m := range matches {
		data.Matches[i] = matchesViewMatchData{
			m,
		}
	}

	s.loadRenderOrError(w, data,
		"matches.tmpl", "matchlist.tmpl", "base.tmpl")
}
<commit_msg>site/matchesview: order matches by time<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package site

import (
	"github.com/bboozzoo/q3stats/controllers/match"
	"github.com/bboozzoo/q3stats/models"
	"log"
	"net/http"
)

type matchesViewMatchData struct {
	models.Match
}

func (s *Site) matchesViewHandler(w http.ResponseWriter, req *http.Request) {
	log.Printf("site matches view handler")

	matches := s.m.ListMatches(match.MatchListParams{
		TimeSort: true,
		SortDesc: true,
	})

	data := struct {
		Matches []matchesViewMatchData
	}{
		make([]matchesViewMatchData, len(matches)),
	}
	for i, m := range matches {
		data.Matches[i] = matchesViewMatchData{
			m,
		}
	}

	s.loadRenderOrError(w, data,
		"matches.tmpl", "matchlist.tmpl", "base.tmpl")
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/pachyderm/pachyderm/src/client"
	ppsclient "github.com/pachyderm/pachyderm/src/client/pps"
	"github.com/pachyderm/pachyderm/src/server/pfs/fuse"
	"github.com/pachyderm/pachyderm/src/server/pkg/cmd"
	"github.com/pachyderm/pachyderm/src/server/pkg/sync"
	ppsserver "github.com/pachyderm/pachyderm/src/server/pps"

	"github.com/spf13/cobra"
	"go.pedge.io/env"
	"go.pedge.io/lion"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"
)

const (
	// PFSInputPrefix is where the input repos reside
	PFSInputPrefix = "/pfs"
	// PFSOutputPrefix is where the output data resides
	PFSOutputPrefix = "/pfs/out"
	// FUSEMountPoint is where we mount FUSE
	FUSEMountPoint = "/pfs/fuse"
)

type appEnv struct {
	PachydermAddress string `env:"PACHD_PORT_650_TCP_ADDR,required"`
	PodName          string `env:"PPS_POD_NAME,required"`
}

func main() {
	env.Main(do, &appEnv{})
}

func downloadInput(c *client.APIClient, commitMounts []*fuse.CommitMount) error {
	var g errgroup.Group
	for _, commitMount := range commitMounts {
		commitMount := commitMount
		if commitMount.Alias == "prev" || commitMount.Alias == "out" {
			continue
		}
		g.Go(func() error {
			return sync.Pull(context.Background(), c.PfsAPIClient, filepath.Join(PFSInputPrefix, commitMount.Commit.Repo.Name), commitMount.Commit, commitMount.DiffMethod, commitMount.Shard)
		})
	}
	return g.Wait()
}

func uploadOutput(c *client.APIClient, out *fuse.CommitMount, overwrite bool) error {
	return sync.Push(context.Background(), c.PfsAPIClient, PFSOutputPrefix, out.Commit, overwrite)
}

func do(appEnvObj interface{}) error {
	appEnv := appEnvObj.(*appEnv)
	rootCmd := &cobra.Command{
		Use:   os.Args[0] + " job-id",
		Short: `Pachyderm job-shim, coordinates with ppsd to create an output commit and run user work.`,
		Long:  `Pachyderm job-shim, coordinates with ppsd to create an output commit and run user work.`,
		Run: cmd.RunFixedArgs(1, func(args []string) (retErr error) {
			ppsClient, err := ppsserver.NewInternalPodAPIClientFromAddress(fmt.Sprintf("%v:650", appEnv.PachydermAddress))
			if err != nil {
				return err
			}
			response, err := ppsClient.StartPod(
				context.Background(),
				&ppsserver.StartPodRequest{
					Job: &ppsclient.Job{
						ID: args[0],
					},
					PodName: appEnv.PodName,
				})
			if err != nil {
				lion.Errorf("error from StartPod: %s", err.Error())
				return err
			}

			// Start sending ContinuePod to PPS to signal that we are alive
			go func() {
				tick := time.Tick(10 * time.Second)
				for {
					<-tick
					res, err := ppsClient.ContinuePod(
						context.Background(),
						&ppsserver.ContinuePodRequest{
							ChunkID: response.ChunkID,
							PodName: appEnv.PodName,
						},
					)
					if err != nil {
						lion.Errorf("error from ContinuePod: %s", err.Error())
					}
					if res != nil && res.Exit {
						os.Exit(0)
					}
				}
			}()

			if response.Transform.Debug {
				lion.SetLevel(lion.LevelDebug)
			}
			// We want to make sure that we only send FinishPod once.
			// The most bulletproof way would be to check that on server side,
			// but this is easier.
			var finished bool
			// Make sure that we call FinishPod even if something caused a panic
			defer func() {
				if r := recover(); r != nil && !finished {
					fmt.Println("job shim crashed; this is like a bug in pachyderm")
					if _, err := ppsClient.FinishPod(
						context.Background(),
						&ppsserver.FinishPodRequest{
							ChunkID: response.ChunkID,
							PodName: appEnv.PodName,
							Success: false,
						},
					); err != nil && retErr == nil {
						retErr = err
					}
				}
			}()

			c, err := client.NewFromAddress(fmt.Sprintf("%v:650", appEnv.PachydermAddress))
			if err != nil {
				return err
			}

			if err := downloadInput(c, response.CommitMounts); err != nil {
				return err
			}

			// Create /pfs/out
			if err := os.MkdirAll(PFSOutputPrefix, 0777); err != nil {
				return err
			}

			// mount /pfs/fuse
			// right now we only have /pfs/fuse/prev
			var mounts []*fuse.CommitMount
			for _, m := range response.CommitMounts {
				if m.Alias == "prev" {
					mounts = append(mounts, m)
				}
			}
			mounter := fuse.NewMounter(appEnv.PachydermAddress, c)
			ready := make(chan bool)
			errCh := make(chan error)
			go func() {
				if err := mounter.MountAndCreate(
					FUSEMountPoint,
					nil,
					mounts,
					ready,
					response.Transform.Debug,
					false,
				); err != nil {
					errCh <- err
				}
			}()
			select {
			case <-ready:
			case err := <-errCh:
				return err
			}
			defer func() {
				if err := mounter.Unmount(FUSEMountPoint); err != nil && retErr == nil {
					retErr = err
				}
			}()

			var readers []io.Reader
			for _, line := range response.Transform.Stdin {
				readers = append(readers, strings.NewReader(line+"\n"))
			}
			if len(response.Transform.Cmd) == 0 {
				fmt.Println("unable to run; a cmd needs to be provided")
				if _, err := ppsClient.FinishPod(
					context.Background(),
					&ppsserver.FinishPodRequest{
						ChunkID: response.ChunkID,
						PodName: appEnv.PodName,
						Success: false,
					},
				); err != nil {
					return err
				}
				finished = true
				return
			}

			cmdCh := make(chan bool)
			go func() {
				cmd := exec.Command(response.Transform.Cmd[0], response.Transform.Cmd[1:]...)
				cmd.Stdin = io.MultiReader(readers...)
				cmd.Stdout = os.Stdout
				cmd.Stderr = os.Stderr
				success := true
				if err := cmd.Run(); err != nil {
					success = false
					if exiterr, ok := err.(*exec.ExitError); ok {
						if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
							for _, returnCode := range response.Transform.AcceptReturnCode {
								if int(returnCode) == status.ExitStatus() {
									success = true
								}
							}
						}
					}
					if !success {
						fmt.Fprintf(os.Stderr, "Error from exec: %s\n", err.Error())
					}
				}
				cmdCh <- success
			}()

			success := <-cmdCh
			var outputMount *fuse.CommitMount
			for _, c := range response.CommitMounts {
				if c.Alias == "out" {
					outputMount = c
					break
				}
			}
			if err := uploadOutput(c, outputMount, response.Transform.Overwrite); err != nil {
				fmt.Printf("err from uploading output: %s\n", err)
				success = false
			}

			res, err := ppsClient.FinishPod(
				context.Background(),
				&ppsserver.FinishPodRequest{
					ChunkID: response.ChunkID,
					PodName: appEnv.PodName,
					Success: success,
				},
			)
			if err != nil {
				return err
			}
			finished = true
			if res.Fail {
				return errors.New("restarting")
			}
			return nil
		}),
	}

	return rootCmd.Execute()
}
<commit_msg>address comment<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/pachyderm/pachyderm/src/client"
	ppsclient "github.com/pachyderm/pachyderm/src/client/pps"
	"github.com/pachyderm/pachyderm/src/server/pfs/fuse"
	"github.com/pachyderm/pachyderm/src/server/pkg/cmd"
	"github.com/pachyderm/pachyderm/src/server/pkg/sync"
	ppsserver "github.com/pachyderm/pachyderm/src/server/pps"

	"github.com/spf13/cobra"
	"go.pedge.io/env"
	"go.pedge.io/lion"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"
)

const (
	// PFSInputPrefix is where the input repos reside
	PFSInputPrefix = "/pfs"
	// PFSOutputPrefix is where the output data resides
	PFSOutputPrefix = "/pfs/out"
	// FUSEMountPoint is where we mount FUSE
	FUSEMountPoint = "/pfs/fuse"
)

type appEnv struct {
	PachydermAddress string `env:"PACHD_PORT_650_TCP_ADDR,required"`
	PodName          string `env:"PPS_POD_NAME,required"`
}

func main() {
	env.Main(do, &appEnv{})
}

func downloadInput(c *client.APIClient, commitMounts []*fuse.CommitMount) error {
	var g errgroup.Group
	for _, commitMount := range commitMounts {
		commitMount := commitMount
		if commitMount.Alias == "prev" || commitMount.Alias == "out" {
			continue
		}
		g.Go(func() error {
			return sync.Pull(context.Background(), c.PfsAPIClient, filepath.Join(PFSInputPrefix, commitMount.Commit.Repo.Name), commitMount.Commit, commitMount.DiffMethod, commitMount.Shard)
		})
	}
	return g.Wait()
}

func uploadOutput(c *client.APIClient, out *fuse.CommitMount, overwrite bool) error {
	return sync.Push(context.Background(), c.PfsAPIClient, PFSOutputPrefix, out.Commit, overwrite)
}

func do(appEnvObj interface{}) error {
	appEnv := appEnvObj.(*appEnv)
	rootCmd := &cobra.Command{
		Use:   os.Args[0] + " job-id",
		Short: `Pachyderm job-shim, coordinates with ppsd to create an output commit and run user work.`,
		Long:  `Pachyderm job-shim, coordinates with ppsd to create an output commit and run user work.`,
		Run: cmd.RunFixedArgs(1, func(args []string) (retErr error) {
			ppsClient, err := ppsserver.NewInternalPodAPIClientFromAddress(fmt.Sprintf("%v:650", appEnv.PachydermAddress))
			if err != nil {
				return err
			}
			response, err := ppsClient.StartPod(
				context.Background(),
				&ppsserver.StartPodRequest{
					Job: &ppsclient.Job{
						ID: args[0],
					},
					PodName: appEnv.PodName,
				})
			if err != nil {
				lion.Errorf("error from StartPod: %s", err.Error())
				return err
			}

			// Start sending ContinuePod to PPS to signal that we are alive
			exitCh := make(chan struct{})
			go func() {
				tick := time.Tick(10 * time.Second)
				for {
					<-tick
					res, err := ppsClient.ContinuePod(
						context.Background(),
						&ppsserver.ContinuePodRequest{
							ChunkID: response.ChunkID,
							PodName: appEnv.PodName,
						},
					)
					if err != nil {
						lion.Errorf("error from ContinuePod: %s", err.Error())
					}
					if res != nil && res.Exit {
						select {
						case exitCh <- struct{}{}:
							// If someone received this signal, then they are
							// responsible to exiting the program and release
							// all resources.
							return
						default:
							// Otherwise, we just terminate the program.
							os.Exit(0)
						}
					}
				}
			}()

			if response.Transform.Debug {
				lion.SetLevel(lion.LevelDebug)
			}
			// We want to make sure that we only send FinishPod once.
			// The most bulletproof way would be to check that on server side,
			// but this is easier.
			var finished bool
			// Make sure that we call FinishPod even if something caused a panic
			defer func() {
				if r := recover(); r != nil && !finished {
					fmt.Println("job shim crashed; this is like a bug in pachyderm")
					if _, err := ppsClient.FinishPod(
						context.Background(),
						&ppsserver.FinishPodRequest{
							ChunkID: response.ChunkID,
							PodName: appEnv.PodName,
							Success: false,
						},
					); err != nil && retErr == nil {
						retErr = err
					}
				}
			}()

			c, err := client.NewFromAddress(fmt.Sprintf("%v:650", appEnv.PachydermAddress))
			if err != nil {
				return err
			}

			if err := downloadInput(c, response.CommitMounts); err != nil {
				return err
			}

			// Create /pfs/out
			if err := os.MkdirAll(PFSOutputPrefix, 0777); err != nil {
				return err
			}

			// mount /pfs/fuse
			// right now we only have /pfs/fuse/prev
			var mounts []*fuse.CommitMount
			for _, m := range response.CommitMounts {
				if m.Alias == "prev" {
					mounts = append(mounts, m)
				}
			}
			mounter := fuse.NewMounter(appEnv.PachydermAddress, c)
			ready := make(chan bool)
			errCh := make(chan error)
			go func() {
				if err := mounter.MountAndCreate(
					FUSEMountPoint,
					nil,
					mounts,
					ready,
					response.Transform.Debug,
					false,
				); err != nil {
					errCh <- err
				}
			}()
			select {
			case <-ready:
			case err := <-errCh:
				return err
			}
			defer func() {
				if err := mounter.Unmount(FUSEMountPoint); err != nil && retErr == nil {
					retErr = err
				}
			}()

			var readers []io.Reader
			for _, line := range response.Transform.Stdin {
				readers = append(readers, strings.NewReader(line+"\n"))
			}
			if len(response.Transform.Cmd) == 0 {
				fmt.Println("unable to run; a cmd needs to be provided")
				if _, err := ppsClient.FinishPod(
					context.Background(),
					&ppsserver.FinishPodRequest{
						ChunkID: response.ChunkID,
						PodName: appEnv.PodName,
						Success: false,
					},
				); err != nil {
					return err
				}
				finished = true
				return
			}

			cmdCh := make(chan bool)
			go func() {
				cmd := exec.Command(response.Transform.Cmd[0], response.Transform.Cmd[1:]...)
				cmd.Stdin = io.MultiReader(readers...)
				cmd.Stdout = os.Stdout
				cmd.Stderr = os.Stderr
				success := true
				if err := cmd.Run(); err != nil {
					success = false
					if exiterr, ok := err.(*exec.ExitError); ok {
						if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
							for _, returnCode := range response.Transform.AcceptReturnCode {
								if int(returnCode) == status.ExitStatus() {
									success = true
								}
							}
						}
					}
					if !success {
						fmt.Fprintf(os.Stderr, "Error from exec: %s\n", err.Error())
					}
				}
				cmdCh <- success
			}()

			var success bool
			select {
			case <-exitCh:
				return nil
			case success = <-cmdCh:
			}
			var outputMount *fuse.CommitMount
			for _, c := range response.CommitMounts {
				if c.Alias == "out" {
					outputMount = c
					break
				}
			}
			if err := uploadOutput(c, outputMount, response.Transform.Overwrite); err != nil {
				fmt.Printf("err from uploading output: %s\n", err)
				success = false
			}

			res, err := ppsClient.FinishPod(
				context.Background(),
				&ppsserver.FinishPodRequest{
					ChunkID: response.ChunkID,
					PodName: appEnv.PodName,
					Success: success,
				},
			)
			if err != nil {
				return err
			}
			finished = true
			if res.Fail {
				return errors.New("restarting")
			}
			return nil
		}),
	}

	return rootCmd.Execute()
}
<|endoftext|>


<commit_before>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2015 Andrii Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

import (
    "sync"
    "time"

    "sippy/headers"
    "sippy/net"
    "sippy/time"
    "sippy/types"
)

type clientTransaction struct {
    *baseTransaction
    teB             *Timeout
    teC             *Timeout
    teG             *Timeout
    r408            sippy_types.SipResponse
    resp_receiver   sippy_types.ResponseReceiver
    expires         time.Duration
    ack             sippy_types.SipRequest
    outbound_proxy  *sippy_net.HostPort
    cancel          sippy_types.SipRequest
    cancelPending   bool
    uack            bool
    ack_rAddr       *sippy_net.HostPort
    ack_checksum    string
    before_request_sent func(sippy_types.SipRequest)
    ack_rparams_present bool
    ack_rTarget     *sippy_header.SipURL
    ack_routes      []*sippy_header.SipRoute
    on_send_complete func()
}

func NewClientTransactionObj(req sippy_types.SipRequest, tid *sippy_header.TID, userv sippy_net.Transport, data []byte, sip_tm *sipTransactionManager, resp_receiver sippy_types.ResponseReceiver, session_lock sync.Locker, address *sippy_net.HostPort, req_out_cb func(sippy_types.SipRequest)) (*clientTransaction, error) {
    var r408 sippy_types.SipResponse = nil
    var err error

    if resp_receiver != nil {
        r408 = req.GenResponse(408, "Request Timeout", /*body*/ nil, /*server*/ nil)
    }
    expires := 32 * time.Second
    needack := false
    var ack, cancel sippy_types.SipRequest
    if req.GetMethod() == "INVITE" {
        expires = 300 * time.Second
        if req.GetExpires() != nil {
            exp, err := req.GetExpires().GetBody()
            if err == nil && exp.Number > 0 {
                expires = time.Duration(exp.Number) * time.Second
            }
        }
        needack = true
        if ack, err = req.GenACK(nil); err != nil {
            return nil, err
        }
        if cancel, err = req.GenCANCEL(); err != nil {
            return nil, err
        }
    }
    self := &clientTransaction{
        resp_receiver   : resp_receiver,
        cancelPending   : false,
        r408            : r408,
        expires         : expires,
        ack             : ack,
        cancel          : cancel,
        uack            : false,
        before_request_sent : req_out_cb,
        ack_rparams_present : false,
    }
    self.baseTransaction = newBaseTransaction(session_lock, tid, userv, sip_tm, address, data, needack, sip_tm.config.ErrorLogger())
    return self, nil
}

func (self *clientTransaction) SetOnSendComplete(fn func()) {
    self.on_send_complete = fn
}

func (self *clientTransaction) StartTimers() {
    self.startTeA()
    self.startTeB(32 * time.Second)
}

func (self *clientTransaction) cleanup() {
    self.baseTransaction.cleanup()
    self.ack = nil
    self.resp_receiver = nil
    if self.teB != nil { self.teB.Cancel(); self.teB = nil }
    if self.teC != nil { self.teC.Cancel(); self.teC = nil }
    if self.teG != nil { self.teG.Cancel(); self.teG = nil }
    self.r408 = nil
    self.cancel = nil
}

func (self *clientTransaction) SetOutboundProxy(outbound_proxy *sippy_net.HostPort) {
    self.outbound_proxy = outbound_proxy
}

func (self *clientTransaction) startTeC() {
    if self.teC != nil {
        self.teC.Cancel()
    }
    self.teC = StartTimeout(self.timerC, self.lock, 32 * time.Second, 1, self.logger)
}

func (self *clientTransaction) timerB() {
    if self.sip_tm == nil {
        return
    }
    //println("timerB", self.tid.String())
    self.cancelTeA()
    self.cancelTeB()
    self.state = TERMINATED
    self.startTeC()
    rtime, _ := sippy_time.NewMonoTime()
    if self.r408 != nil {
        self.r408.SetRtime(rtime)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(self.r408, self)
    }
}

func (self *clientTransaction) timerC() {
    if self.sip_tm == nil {
        return
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) timerG() {
    if self.sip_tm == nil {
        return
    }
    self.teG = nil
    if self.state == UACK {
        self.logger.Error("INVITE transaction stuck in the UACK state, possible UAC bug")
    }
}

func (self *clientTransaction) cancelTeB() {
    if self.teB != nil {
        self.teB.Cancel()
        self.teB = nil
    }
}

func (self *clientTransaction) startTeB(timeout time.Duration) {
    if self.teB != nil {
        self.teB.Cancel()
    }
    self.teB = StartTimeout(self.timerB, self.lock, timeout, 1, self.logger)
}

func (self *clientTransaction) IncomingResponse(resp sippy_types.SipResponse, checksum string) {
    if self.sip_tm == nil {
        return
    }
    // In those two states upper level already notified, only do ACK retransmit
    // if needed
    if self.state == TERMINATED {
        return
    }
    if self.state == TRYING {
        // Stop timers
        self.cancelTeA()
    }
    self.cancelTeB()
    if resp.GetSCodeNum() < 200 {
        self.process_provisional_response(checksum, resp)
    } else {
        self.process_final_response(checksum, resp)
    }
}

func (self *clientTransaction) process_provisional_response(checksum string, resp sippy_types.SipResponse) {
    // Privisional response - leave everything as is, except that
    // change state and reload timeout timer
    if self.state == TRYING {
        self.state = RINGING
        if self.cancelPending {
            self.sip_tm.BeginNewClientTransaction(self.cancel, nil, self.lock, nil, self.userv, self.before_request_sent)
            self.cancelPending = false
        }
    }
    self.startTeB(self.expires)
    self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
}

func (self *clientTransaction) process_final_response(checksum string, resp sippy_types.SipResponse) {
    // Final response - notify upper layer and remove transaction
    if self.needack {
        // Prepare and send ACK if necessary
        fcode := resp.GetSCodeNum()
        to_body, err := resp.GetTo().GetBody()
        if err != nil {
            self.sip_tm.config.ErrorLogger().Debug(err.Error())
            return
        }
        tag := to_body.GetTag()
        if tag != "" {
            to_body, err = self.ack.GetTo().GetBody()
            if err != nil {
                self.sip_tm.config.ErrorLogger().Debug(err.Error())
                return
            }
            to_body.SetTag(tag)
        }
        var rAddr *sippy_net.HostPort
        var rTarget *sippy_header.SipURL
        if resp.GetSCodeNum() >= 200 && resp.GetSCodeNum() < 300 {
            // Some hairy code ahead
            if len(resp.GetContacts()) > 0 {
                var contact *sippy_header.SipAddress
                contact, err = resp.GetContacts()[0].GetBody()
                if err != nil {
                    self.sip_tm.config.ErrorLogger().Debug(err.Error())
                    return
                }
                rTarget = contact.GetUrl().GetCopy()
            } else {
                rTarget = nil
            }
            var routes []*sippy_header.SipRoute
            if ! self.ack_rparams_present {
                routes = make([]*sippy_header.SipRoute, len(resp.GetRecordRoutes()))
                for idx, r := range resp.GetRecordRoutes() {
                    r2 := r.AsSipRoute() // r.getCopy()
                    routes[len(resp.GetRecordRoutes()) - 1 + idx] = r2 // reverse order
                }
                if len(routes) > 0 {
                    var r0 *sippy_header.SipAddress
                    r0, err = routes[0].GetBody()
                    if err != nil {
                        self.sip_tm.config.ErrorLogger().Debug(err.Error())
                        return
                    }
                    if ! r0.GetUrl().Lr {
                        if rTarget != nil {
                            routes = append(routes, sippy_header.NewSipRoute(sippy_header.NewSipAddress("", rTarget), self.sip_tm.config))
                        }
                        rTarget = r0.GetUrl()
                        routes = routes[1:]
                        rAddr = rTarget.GetAddr(self.sip_tm.config)
                    } else {
                        rAddr = r0.GetUrl().GetAddr(self.sip_tm.config)
                    }
                } else if rTarget != nil {

                    rAddr = rTarget.GetAddr(self.sip_tm.config)
                }
                if rTarget != nil {
                    self.ack.SetRURI(rTarget)
                }
                if self.outbound_proxy != nil {
                    routes = append([]*sippy_header.SipRoute{ sippy_header.NewSipRoute(sippy_header.NewSipAddress("", sippy_header.NewSipURL("", self.outbound_proxy.Host, self.outbound_proxy.Port, true)), self.sip_tm.config) }, routes...)
                    rAddr = self.outbound_proxy
                }
            } else {
                rAddr, rTarget, routes = self.ack_rAddr, self.ack_rTarget, self.ack_routes
            }
            self.ack.SetRoutes(routes)
        }
        if fcode >= 200 && fcode < 300 {
            var via0 *sippy_header.SipViaBody
            if via0, err = self.ack.GetVias()[0].GetBody(); err != nil {
                self.sip_tm.config.ErrorLogger().Debug("error parsing via: " + err.Error())
                return
            }
            via0.GenBranch()
        }
        if rAddr == nil {
            rAddr = self.address
        }
        if ! self.uack {
            self.sip_tm.transmitMsg(self.userv, self.ack, rAddr, checksum, self.tid.CallId)
        } else {
            self.state = UACK
            self.ack_rAddr = rAddr
            self.ack_checksum = checksum
            self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
            self.teG = StartTimeout(self.timerG, self.lock, 64 * time.Second, 1, self.logger)
            return
        }
    } else {
        self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) Cancel(extra_headers ...sippy_header.SipHeader) {
    if self.sip_tm == nil {
        return
    }
    // If we got at least one provisional reply then (state == RINGING)
    // then start CANCEL transaction, otherwise deffer it
    if self.state != RINGING {
        self.cancelPending = true
    } else {
        if extra_headers != nil {
            for _, h := range extra_headers {
                self.cancel.AppendHeader(h)
            }
        }
        self.sip_tm.BeginNewClientTransaction(self.cancel, nil, self.lock, nil, self.userv, self.before_request_sent)
    }
}

func (self *clientTransaction) Lock() {
    self.lock.Lock()
}

func (self *clientTransaction) Unlock() {
    self.lock.Unlock()
}

func (self *clientTransaction) SendACK() {
    if self.teG != nil {
        self.teG.Cancel()
        self.teG = nil
    }
    self.sip_tm.transmitMsg(self.userv, self.ack, self.ack_rAddr, self.ack_checksum, self.tid.CallId)
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) GetACK() sippy_types.SipRequest {
    return self.ack
}

func (self *clientTransaction) SetUAck(uack bool) {
    self.uack = uack
}

func (self *clientTransaction) BeforeRequestSent(req sippy_types.SipRequest) {
    if self.before_request_sent != nil {
        self.before_request_sent(req)
    }
}

func (self *clientTransaction) TransmitData() {
    if self.sip_tm != nil {
        self.sip_tm.transmitDataWithCb(self.userv, self.data, self.address, /*cachesum*/ "", /*call_id =*/ self.tid.CallId, 0, self.on_send_complete)
    }
}

func (self *clientTransaction) SetAckRparams(rAddr *sippy_net.HostPort, rTarget *sippy_header.SipURL, routes []*sippy_header.SipRoute) {
    self.ack_rparams_present = true
    self.ack_rAddr = rAddr
    self.ack_rTarget = rTarget
    self.ack_routes = routes
}
<commit_msg>Fix typo.<commit_after>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2015 Andrii Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

import (
    "sync"
    "time"

    "sippy/headers"
    "sippy/net"
    "sippy/time"
    "sippy/types"
)

type clientTransaction struct {
    *baseTransaction
    teB             *Timeout
    teC             *Timeout
    teG             *Timeout
    r408            sippy_types.SipResponse
    resp_receiver   sippy_types.ResponseReceiver
    expires         time.Duration
    ack             sippy_types.SipRequest
    outbound_proxy  *sippy_net.HostPort
    cancel          sippy_types.SipRequest
    cancelPending   bool
    uack            bool
    ack_rAddr       *sippy_net.HostPort
    ack_checksum    string
    before_request_sent func(sippy_types.SipRequest)
    ack_rparams_present bool
    ack_rTarget     *sippy_header.SipURL
    ack_routes      []*sippy_header.SipRoute
    on_send_complete func()
}

func NewClientTransactionObj(req sippy_types.SipRequest, tid *sippy_header.TID, userv sippy_net.Transport, data []byte, sip_tm *sipTransactionManager, resp_receiver sippy_types.ResponseReceiver, session_lock sync.Locker, address *sippy_net.HostPort, req_out_cb func(sippy_types.SipRequest)) (*clientTransaction, error) {
    var r408 sippy_types.SipResponse = nil
    var err error

    if resp_receiver != nil {
        r408 = req.GenResponse(408, "Request Timeout", /*body*/ nil, /*server*/ nil)
    }
    expires := 32 * time.Second
    needack := false
    var ack, cancel sippy_types.SipRequest
    if req.GetMethod() == "INVITE" {
        expires = 300 * time.Second
        if req.GetExpires() != nil {
            exp, err := req.GetExpires().GetBody()
            if err == nil && exp.Number > 0 {
                expires = time.Duration(exp.Number) * time.Second
            }
        }
        needack = true
        if ack, err = req.GenACK(nil); err != nil {
            return nil, err
        }
        if cancel, err = req.GenCANCEL(); err != nil {
            return nil, err
        }
    }
    self := &clientTransaction{
        resp_receiver   : resp_receiver,
        cancelPending   : false,
        r408            : r408,
        expires         : expires,
        ack             : ack,
        cancel          : cancel,
        uack            : false,
        before_request_sent : req_out_cb,
        ack_rparams_present : false,
    }
    self.baseTransaction = newBaseTransaction(session_lock, tid, userv, sip_tm, address, data, needack, sip_tm.config.ErrorLogger())
    return self, nil
}

func (self *clientTransaction) SetOnSendComplete(fn func()) {
    self.on_send_complete = fn
}

func (self *clientTransaction) StartTimers() {
    self.startTeA()
    self.startTeB(32 * time.Second)
}

func (self *clientTransaction) cleanup() {
    self.baseTransaction.cleanup()
    self.ack = nil
    self.resp_receiver = nil
    if self.teB != nil { self.teB.Cancel(); self.teB = nil }
    if self.teC != nil { self.teC.Cancel(); self.teC = nil }
    if self.teG != nil { self.teG.Cancel(); self.teG = nil }
    self.r408 = nil
    self.cancel = nil
}

func (self *clientTransaction) SetOutboundProxy(outbound_proxy *sippy_net.HostPort) {
    self.outbound_proxy = outbound_proxy
}

func (self *clientTransaction) startTeC() {
    if self.teC != nil {
        self.teC.Cancel()
    }
    self.teC = StartTimeout(self.timerC, self.lock, 32 * time.Second, 1, self.logger)
}

func (self *clientTransaction) timerB() {
    if self.sip_tm == nil {
        return
    }
    //println("timerB", self.tid.String())
    self.cancelTeA()
    self.cancelTeB()
    self.state = TERMINATED
    self.startTeC()
    rtime, _ := sippy_time.NewMonoTime()
    if self.r408 != nil {
        self.r408.SetRtime(rtime)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(self.r408, self)
    }
}

func (self *clientTransaction) timerC() {
    if self.sip_tm == nil {
        return
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) timerG() {
    if self.sip_tm == nil {
        return
    }
    self.teG = nil
    if self.state == UACK {
        self.logger.Error("INVITE transaction stuck in the UACK state, possible UAC bug")
    }
}

func (self *clientTransaction) cancelTeB() {
    if self.teB != nil {
        self.teB.Cancel()
        self.teB = nil
    }
}

func (self *clientTransaction) startTeB(timeout time.Duration) {
    if self.teB != nil {
        self.teB.Cancel()
    }
    self.teB = StartTimeout(self.timerB, self.lock, timeout, 1, self.logger)
}

func (self *clientTransaction) IncomingResponse(resp sippy_types.SipResponse, checksum string) {
    if self.sip_tm == nil {
        return
    }
    // In those two states upper level already notified, only do ACK retransmit
    // if needed
    if self.state == TERMINATED {
        return
    }
    if self.state == TRYING {
        // Stop timers
        self.cancelTeA()
    }
    self.cancelTeB()
    if resp.GetSCodeNum() < 200 {
        self.process_provisional_response(checksum, resp)
    } else {
        self.process_final_response(checksum, resp)
    }
}

func (self *clientTransaction) process_provisional_response(checksum string, resp sippy_types.SipResponse) {
    // Privisional response - leave everything as is, except that
    // change state and reload timeout timer
    if self.state == TRYING {
        self.state = RINGING
        if self.cancelPending {
            self.sip_tm.BeginNewClientTransaction(self.cancel, nil, self.lock, nil, self.userv, self.before_request_sent)
            self.cancelPending = false
        }
    }
    self.startTeB(self.expires)
    self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
}

func (self *clientTransaction) process_final_response(checksum string, resp sippy_types.SipResponse) {
    // Final response - notify upper layer and remove transaction
    if self.needack {
        // Prepare and send ACK if necessary
        fcode := resp.GetSCodeNum()
        to_body, err := resp.GetTo().GetBody()
        if err != nil {
            self.sip_tm.config.ErrorLogger().Debug(err.Error())
            return
        }
        tag := to_body.GetTag()
        if tag != "" {
            to_body, err = self.ack.GetTo().GetBody()
            if err != nil {
                self.sip_tm.config.ErrorLogger().Debug(err.Error())
                return
            }
            to_body.SetTag(tag)
        }
        var rAddr *sippy_net.HostPort
        var rTarget *sippy_header.SipURL
        if resp.GetSCodeNum() >= 200 && resp.GetSCodeNum() < 300 {
            // Some hairy code ahead
            if len(resp.GetContacts()) > 0 {
                var contact *sippy_header.SipAddress
                contact, err = resp.GetContacts()[0].GetBody()
                if err != nil {
                    self.sip_tm.config.ErrorLogger().Debug(err.Error())
                    return
                }
                rTarget = contact.GetUrl().GetCopy()
            } else {
                rTarget = nil
            }
            var routes []*sippy_header.SipRoute
            if ! self.ack_rparams_present {
                routes = make([]*sippy_header.SipRoute, len(resp.GetRecordRoutes()))
                for idx, r := range resp.GetRecordRoutes() {
                    r2 := r.AsSipRoute() // r.getCopy()
                    routes[len(resp.GetRecordRoutes()) - 1 - idx] = r2 // reverse order
                }
                if len(routes) > 0 {
                    var r0 *sippy_header.SipAddress
                    r0, err = routes[0].GetBody()
                    if err != nil {
                        self.sip_tm.config.ErrorLogger().Debug(err.Error())
                        return
                    }
                    if ! r0.GetUrl().Lr {
                        if rTarget != nil {
                            routes = append(routes, sippy_header.NewSipRoute(sippy_header.NewSipAddress("", rTarget), self.sip_tm.config))
                        }
                        rTarget = r0.GetUrl()
                        routes = routes[1:]
                        rAddr = rTarget.GetAddr(self.sip_tm.config)
                    } else {
                        rAddr = r0.GetUrl().GetAddr(self.sip_tm.config)
                    }
                } else if rTarget != nil {

                    rAddr = rTarget.GetAddr(self.sip_tm.config)
                }
                if rTarget != nil {
                    self.ack.SetRURI(rTarget)
                }
                if self.outbound_proxy != nil {
                    routes = append([]*sippy_header.SipRoute{ sippy_header.NewSipRoute(sippy_header.NewSipAddress("", sippy_header.NewSipURL("", self.outbound_proxy.Host, self.outbound_proxy.Port, true)), self.sip_tm.config) }, routes...)
                    rAddr = self.outbound_proxy
                }
            } else {
                rAddr, rTarget, routes = self.ack_rAddr, self.ack_rTarget, self.ack_routes
            }
            self.ack.SetRoutes(routes)
        }
        if fcode >= 200 && fcode < 300 {
            var via0 *sippy_header.SipViaBody
            if via0, err = self.ack.GetVias()[0].GetBody(); err != nil {
                self.sip_tm.config.ErrorLogger().Debug("error parsing via: " + err.Error())
                return
            }
            via0.GenBranch()
        }
        if rAddr == nil {
            rAddr = self.address
        }
        if ! self.uack {
            self.sip_tm.transmitMsg(self.userv, self.ack, rAddr, checksum, self.tid.CallId)
        } else {
            self.state = UACK
            self.ack_rAddr = rAddr
            self.ack_checksum = checksum
            self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
            self.teG = StartTimeout(self.timerG, self.lock, 64 * time.Second, 1, self.logger)
            return
        }
    } else {
        self.sip_tm.rcache_set_call_id(checksum, self.tid.CallId)
    }
    if self.resp_receiver != nil {
        self.resp_receiver.RecvResponse(resp, self)
    }
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) Cancel(extra_headers ...sippy_header.SipHeader) {
    if self.sip_tm == nil {
        return
    }
    // If we got at least one provisional reply then (state == RINGING)
    // then start CANCEL transaction, otherwise deffer it
    if self.state != RINGING {
        self.cancelPending = true
    } else {
        if extra_headers != nil {
            for _, h := range extra_headers {
                self.cancel.AppendHeader(h)
            }
        }
        self.sip_tm.BeginNewClientTransaction(self.cancel, nil, self.lock, nil, self.userv, self.before_request_sent)
    }
}

func (self *clientTransaction) Lock() {
    self.lock.Lock()
}

func (self *clientTransaction) Unlock() {
    self.lock.Unlock()
}

func (self *clientTransaction) SendACK() {
    if self.teG != nil {
        self.teG.Cancel()
        self.teG = nil
    }
    self.sip_tm.transmitMsg(self.userv, self.ack, self.ack_rAddr, self.ack_checksum, self.tid.CallId)
    self.sip_tm.tclient_del(self.tid)
    self.cleanup()
}

func (self *clientTransaction) GetACK() sippy_types.SipRequest {
    return self.ack
}

func (self *clientTransaction) SetUAck(uack bool) {
    self.uack = uack
}

func (self *clientTransaction) BeforeRequestSent(req sippy_types.SipRequest) {
    if self.before_request_sent != nil {
        self.before_request_sent(req)
    }
}

func (self *clientTransaction) TransmitData() {
    if self.sip_tm != nil {
        self.sip_tm.transmitDataWithCb(self.userv, self.data, self.address, /*cachesum*/ "", /*call_id =*/ self.tid.CallId, 0, self.on_send_complete)
    }
}

func (self *clientTransaction) SetAckRparams(rAddr *sippy_net.HostPort, rTarget *sippy_header.SipURL, routes []*sippy_header.SipRoute) {
    self.ack_rparams_present = true
    self.ack_rAddr = rAddr
    self.ack_rTarget = rTarget
    self.ack_routes = routes
}
<|endoftext|>


<commit_before>// +build !windows

/*
   Copyright The containerd Authors.

   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 run

import (
	gocontext "context"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/containerd/contrib/nvidia"
	"github.com/containerd/containerd/contrib/seccomp"
	"github.com/containerd/containerd/oci"
	"github.com/containerd/containerd/platforms"
	"github.com/containerd/containerd/runtime/v2/runc/options"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

var platformRunFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "runc-binary",
		Usage: "specify runc-compatible binary",
	},
	cli.BoolFlag{
		Name:  "runc-systemd-cgroup",
		Usage: "start runc with systemd cgroup manager",
	},
	cli.StringFlag{
		Name:  "uidmap",
		Usage: "run inside a user namespace with the specified UID mapping range; specified with the format `container-uid:host-uid:length`",
	},
	cli.StringFlag{
		Name:  "gidmap",
		Usage: "run inside a user namespace with the specified GID mapping range; specified with the format `container-gid:host-gid:length`",
	},
}

// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
	var (
		id     string
		config = context.IsSet("config")
	)
	if config {
		id = context.Args().First()
	} else {
		id = context.Args().Get(1)
	}

	var (
		opts  []oci.SpecOpts
		cOpts []containerd.NewContainerOpts
		spec  containerd.NewContainerOpts
	)

	cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
	if config {
		opts = append(opts, oci.WithSpecFromFile(context.String("config")))
	} else {
		var (
			ref = context.Args().First()
			//for container's id is Args[1]
			args = context.Args()[2:]
		)
		opts = append(opts, oci.WithDefaultSpec(), oci.WithDefaultUnixDevices)
		if ef := context.String("env-file"); ef != "" {
			opts = append(opts, oci.WithEnvFile(ef))
		}
		opts = append(opts, oci.WithEnv(context.StringSlice("env")))
		opts = append(opts, withMounts(context))

		if context.Bool("rootfs") {
			rootfs, err := filepath.Abs(ref)
			if err != nil {
				return nil, err
			}
			opts = append(opts, oci.WithRootFSPath(rootfs))
		} else {
			snapshotter := context.String("snapshotter")
			var image containerd.Image
			i, err := client.ImageService().Get(ctx, ref)
			if err != nil {
				return nil, err
			}
			if ps := context.String("platform"); ps != "" {
				platform, err := platforms.Parse(ps)
				if err != nil {
					return nil, err
				}
				image = containerd.NewImageWithPlatform(client, i, platforms.Only(platform))
			} else {
				image = containerd.NewImage(client, i)
			}

			unpacked, err := image.IsUnpacked(ctx, snapshotter)
			if err != nil {
				return nil, err
			}
			if !unpacked {
				if err := image.Unpack(ctx, snapshotter); err != nil {
					return nil, err
				}
			}
			opts = append(opts, oci.WithImageConfig(image))
			cOpts = append(cOpts,
				containerd.WithImage(image),
				containerd.WithSnapshotter(snapshotter))
			if uidmap, gidmap := context.String("uidmap"), context.String("gidmap"); uidmap != "" && gidmap != "" {
				uidMap, err := parseIDMapping(uidmap)
				if err != nil {
					return nil, err
				}
				gidMap, err := parseIDMapping(gidmap)
				if err != nil {
					return nil, err
				}
				opts = append(opts,
					oci.WithUserNamespace([]specs.LinuxIDMapping{uidMap}, []specs.LinuxIDMapping{gidMap}))
				if context.Bool("read-only") {
					cOpts = append(cOpts, containerd.WithRemappedSnapshotView(id, image, uidMap.HostID, gidMap.HostID))
				} else {
					cOpts = append(cOpts, containerd.WithRemappedSnapshot(id, image, uidMap.HostID, gidMap.HostID))
				}
			} else {
				// Even when "read-only" is set, we don't use KindView snapshot here. (#1495)
				// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
				// after creating some mount points on demand.
				cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
			}
			cOpts = append(cOpts, containerd.WithImageStopSignal(image, "SIGTERM"))
		}
		if context.Bool("read-only") {
			opts = append(opts, oci.WithRootFSReadonly())
		}
		if len(args) > 0 {
			opts = append(opts, oci.WithProcessArgs(args...))
		}
		if cwd := context.String("cwd"); cwd != "" {
			opts = append(opts, oci.WithProcessCwd(cwd))
		}
		if context.Bool("tty") {
			opts = append(opts, oci.WithTTY)
		}
		if context.Bool("privileged") {
			opts = append(opts, oci.WithPrivileged, oci.WithAllDevicesAllowed, oci.WithHostDevices)
		}
		if context.Bool("net-host") {
			opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
		}
		if context.Bool("seccomp") {
			opts = append(opts, seccomp.WithDefaultProfile())
		}

		joinNs := context.StringSlice("with-ns")
		for _, ns := range joinNs {
			parts := strings.Split(ns, ":")
			if len(parts) != 2 {
				return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'")
			}
			if !validNamespace(parts[0]) {
				return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0])
			}
			opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{
				Type: specs.LinuxNamespaceType(parts[0]),
				Path: parts[1],
			}))
		}
		if context.IsSet("gpus") {
			opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities))
		}
		if context.IsSet("allow-new-privs") {
			opts = append(opts, oci.WithNewPrivileges)
		}
		if context.IsSet("cgroup") {
			// NOTE: can be set to "" explicitly for disabling cgroup.
			opts = append(opts, oci.WithCgroup(context.String("cgroup")))
		}
		limit := context.Uint64("memory-limit")
		if limit != 0 {
			opts = append(opts, oci.WithMemoryLimit(limit))
		}
		for _, dev := range context.StringSlice("device") {
			opts = append(opts, oci.WithLinuxDevice(dev, "rwm"))
		}
	}

	runtimeOpts := &options.Options{}
	if runcBinary := context.String("runc-binary"); runcBinary != "" {
		if context.String("runtime") == "io.containerd.runc.v2" {
			runtimeOpts.BinaryName = runcBinary
		} else {
			return nil, errors.New("specifying runc-binary is only supported for \"io.containerd.runc.v2\" runtime")
		}
	}
	if context.Bool("runc-systemd-cgroup") {
		if context.String("runtime") == "io.containerd.runc.v2" {
			if context.String("cgroup") == "" {
				// runc maps "machine.slice:foo:deadbeef" to "/machine.slice/foo-deadbeef.scope"
				return nil, errors.New("option --runc-systemd-cgroup requires --cgroup to be set, e.g. \"machine.slice:foo:deadbeef\"")
			}
			runtimeOpts.SystemdCgroup = true
		} else {
			return nil, errors.New("specifying runc-systemd-cgroup is only supported for \"io.containerd.runc.v2\" runtime")
		}
	}
	cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), runtimeOpts))

	opts = append(opts, oci.WithAnnotations(commands.LabelArgs(context.StringSlice("label"))))
	var s specs.Spec
	spec = containerd.WithSpec(&s, opts...)

	cOpts = append(cOpts, spec)

	// oci.WithImageConfig (WithUsername, WithUserID) depends on access to rootfs for resolving via
	// the /etc/{passwd,group} files. So cOpts needs to have precedence over opts.
	return client.NewContainer(ctx, id, cOpts...)
}

func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
	var (
		tOpts []containerd.NewTaskOpts
	)
	if context.Bool("no-pivot") {
		tOpts = append(tOpts, containerd.WithNoPivotRoot)
	}
	if uidmap := context.String("uidmap"); uidmap != "" {
		uidMap, err := parseIDMapping(uidmap)
		if err != nil {
			logrus.WithError(err).Warn("unable to parse uidmap; defaulting to uid 0 IO ownership")
		}
		tOpts = append(tOpts, containerd.WithUIDOwner(uidMap.HostID))
	}
	if gidmap := context.String("gidmap"); gidmap != "" {
		gidMap, err := parseIDMapping(gidmap)
		if err != nil {
			logrus.WithError(err).Warn("unable to parse gidmap; defaulting to gid 0 IO ownership")
		}
		tOpts = append(tOpts, containerd.WithGIDOwner(gidMap.HostID))
	}
	return tOpts
}

func parseIDMapping(mapping string) (specs.LinuxIDMapping, error) {
	parts := strings.Split(mapping, ":")
	if len(parts) != 3 {
		return specs.LinuxIDMapping{}, errors.New("user namespace mappings require the format `container-id:host-id:size`")
	}
	cID, err := strconv.ParseUint(parts[0], 0, 16)
	if err != nil {
		return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid container id for user namespace remapping")
	}
	hID, err := strconv.ParseUint(parts[1], 0, 16)
	if err != nil {
		return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid host id for user namespace remapping")
	}
	size, err := strconv.ParseUint(parts[2], 0, 16)
	if err != nil {
		return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid size for user namespace remapping")
	}
	return specs.LinuxIDMapping{
		ContainerID: uint32(cID),
		HostID:      uint32(hID),
		Size:        uint32(size),
	}, nil
}

func validNamespace(ns string) bool {
	linuxNs := specs.LinuxNamespaceType(ns)
	switch linuxNs {
	case specs.PIDNamespace,
		specs.NetworkNamespace,
		specs.UTSNamespace,
		specs.MountNamespace,
		specs.UserNamespace,
		specs.IPCNamespace,
		specs.CgroupNamespace:
		return true
	default:
		return false
	}
}
<commit_msg>ctr: do not assume runc options by default<commit_after>// +build !windows

/*
   Copyright The containerd Authors.

   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 run

import (
	gocontext "context"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/containerd/contrib/nvidia"
	"github.com/containerd/containerd/contrib/seccomp"
	"github.com/containerd/containerd/oci"
	"github.com/containerd/containerd/platforms"
	"github.com/containerd/containerd/runtime/v2/runc/options"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

var platformRunFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "runc-binary",
		Usage: "specify runc-compatible binary",
	},
	cli.BoolFlag{
		Name:  "runc-systemd-cgroup",
		Usage: "start runc with systemd cgroup manager",
	},
	cli.StringFlag{
		Name:  "uidmap",
		Usage: "run inside a user namespace with the specified UID mapping range; specified with the format `container-uid:host-uid:length`",
	},
	cli.StringFlag{
		Name:  "gidmap",
		Usage: "run inside a user namespace with the specified GID mapping range; specified with the format `container-gid:host-gid:length`",
	},
}

// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
	var (
		id     string
		config = context.IsSet("config")
	)
	if config {
		id = context.Args().First()
	} else {
		id = context.Args().Get(1)
	}

	var (
		opts  []oci.SpecOpts
		cOpts []containerd.NewContainerOpts
		spec  containerd.NewContainerOpts
	)

	cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
	if config {
		opts = append(opts, oci.WithSpecFromFile(context.String("config")))
	} else {
		var (
			ref = context.Args().First()
			//for container's id is Args[1]
			args = context.Args()[2:]
		)
		opts = append(opts, oci.WithDefaultSpec(), oci.WithDefaultUnixDevices)
		if ef := context.String("env-file"); ef != "" {
			opts = append(opts, oci.WithEnvFile(ef))
		}
		opts = append(opts, oci.WithEnv(context.StringSlice("env")))
		opts = append(opts, withMounts(context))

		if context.Bool("rootfs") {
			rootfs, err := filepath.Abs(ref)
			if err != nil {
				return nil, err
			}
			opts = append(opts, oci.WithRootFSPath(rootfs))
		} else {
			snapshotter := context.String("snapshotter")
			var image containerd.Image
			i, err := client.ImageService().Get(ctx, ref)
			if err != nil {
				return nil, err
			}
			if ps := context.String("platform"); ps != "" {
				platform, err := platforms.Parse(ps)
				if err != nil {
					return nil, err
				}
				image = containerd.NewImageWithPlatform(client, i, platforms.Only(platform))
			} else {
				image = containerd.NewImage(client, i)
			}

			unpacked, err := image.IsUnpacked(ctx, snapshotter)
			if err != nil {
				return nil, err
			}
			if !unpacked {
				if err := image.Unpack(ctx, snapshotter); err != nil {
					return nil, err
				}
			}
			opts = append(opts, oci.WithImageConfig(image))
			cOpts = append(cOpts,
				containerd.WithImage(image),
				containerd.WithSnapshotter(snapshotter))
			if uidmap, gidmap := context.String("uidmap"), context.String("gidmap"); uidmap != "" && gidmap != "" {
				uidMap, err := parseIDMapping(uidmap)
				if err != nil {
					return nil, err
				}
				gidMap, err := parseIDMapping(gidmap)
				if err != nil {
					return nil, err
				}
				opts = append(opts,
					oci.WithUserNamespace([]specs.LinuxIDMapping{uidMap}, []specs.LinuxIDMapping{gidMap}))
				if context.Bool("read-only") {
					cOpts = append(cOpts, containerd.WithRemappedSnapshotView(id, image, uidMap.HostID, gidMap.HostID))
				} else {
					cOpts = append(cOpts, containerd.WithRemappedSnapshot(id, image, uidMap.HostID, gidMap.HostID))
				}
			} else {
				// Even when "read-only" is set, we don't use KindView snapshot here. (#1495)
				// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
				// after creating some mount points on demand.
				cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
			}
			cOpts = append(cOpts, containerd.WithImageStopSignal(image, "SIGTERM"))
		}
		if context.Bool("read-only") {
			opts = append(opts, oci.WithRootFSReadonly())
		}
		if len(args) > 0 {
			opts = append(opts, oci.WithProcessArgs(args...))
		}
		if cwd := context.String("cwd"); cwd != "" {
			opts = append(opts, oci.WithProcessCwd(cwd))
		}
		if context.Bool("tty") {
			opts = append(opts, oci.WithTTY)
		}
		if context.Bool("privileged") {
			opts = append(opts, oci.WithPrivileged, oci.WithAllDevicesAllowed, oci.WithHostDevices)
		}
		if context.Bool("net-host") {
			opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
		}
		if context.Bool("seccomp") {
			opts = append(opts, seccomp.WithDefaultProfile())
		}

		joinNs := context.StringSlice("with-ns")
		for _, ns := range joinNs {
			parts := strings.Split(ns, ":")
			if len(parts) != 2 {
				return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'")
			}
			if !validNamespace(parts[0]) {
				return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0])
			}
			opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{
				Type: specs.LinuxNamespaceType(parts[0]),
				Path: parts[1],
			}))
		}
		if context.IsSet("gpus") {
			opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities))
		}
		if context.IsSet("allow-new-privs") {
			opts = append(opts, oci.WithNewPrivileges)
		}
		if context.IsSet("cgroup") {
			// NOTE: can be set to "" explicitly for disabling cgroup.
			opts = append(opts, oci.WithCgroup(context.String("cgroup")))
		}
		limit := context.Uint64("memory-limit")
		if limit != 0 {
			opts = append(opts, oci.WithMemoryLimit(limit))
		}
		for _, dev := range context.StringSlice("device") {
			opts = append(opts, oci.WithLinuxDevice(dev, "rwm"))
		}
	}

	runtimeOpts, err := getRuntimeOptions(context)
	if err != nil {
		return nil, err
	}
	cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), runtimeOpts))

	opts = append(opts, oci.WithAnnotations(commands.LabelArgs(context.StringSlice("label"))))
	var s specs.Spec
	spec = containerd.WithSpec(&s, opts...)

	cOpts = append(cOpts, spec)

	// oci.WithImageConfig (WithUsername, WithUserID) depends on access to rootfs for resolving via
	// the /etc/{passwd,group} files. So cOpts needs to have precedence over opts.
	return client.NewContainer(ctx, id, cOpts...)
}

func getRuncOptions(context *cli.Context) (*options.Options, error) {
	runtimeOpts := &options.Options{}
	if runcBinary := context.String("runc-binary"); runcBinary != "" {
		runtimeOpts.BinaryName = runcBinary
	}
	if context.Bool("runc-systemd-cgroup") {
		if context.String("cgroup") == "" {
			// runc maps "machine.slice:foo:deadbeef" to "/machine.slice/foo-deadbeef.scope"
			return nil, errors.New("option --runc-systemd-cgroup requires --cgroup to be set, e.g. \"machine.slice:foo:deadbeef\"")
		}
		runtimeOpts.SystemdCgroup = true
	}

	return runtimeOpts, nil
}

func getRuntimeOptions(context *cli.Context) (interface{}, error) {
	// validate first
	if (context.String("runc-binary") != "" || context.Bool("runc-systemd-cgroup")) &&
		context.String("runtime") != "io.containerd.runc.v2" {
		return nil, errors.New("specifying runc-binary and runc-systemd-cgroup is only supported for \"io.containerd.runc.v2\" runtime")
	}

	if context.String("runtime") == "io.containerd.runc.v2" {
		return getRuncOptions(context)
	}

	return nil, nil
}

func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
	var (
		tOpts []containerd.NewTaskOpts
	)
	if context.Bool("no-pivot") {
		tOpts = append(tOpts, containerd.WithNoPivotRoot)
	}
	if uidmap := context.String("uidmap"); uidmap != "" {
		uidMap, err := parseIDMapping(uidmap)
		if err != nil {
			logrus.WithError(err).Warn("unable to parse uidmap; defaulting to uid 0 IO ownership")
		}
		tOpts = append(tOpts, containerd.WithUIDOwner(uidMap.HostID))
	}
	if gidmap := context.String("gidmap"); gidmap != "" {
		gidMap, err := parseIDMapping(gidmap)
		if err != nil {
			logrus.WithError(err).Warn("unable to parse gidmap; defaulting to gid 0 IO ownership")
		}
		tOpts = append(tOpts, containerd.WithGIDOwner(gidMap.HostID))
	}
	return tOpts
}

func parseIDMapping(mapping string) (specs.LinuxIDMapping, error) {
	parts := strings.Split(mapping, ":")
	if len(parts) != 3 {
		return specs.LinuxIDMapping{}, errors.New("user namespace mappings require the format `container-id:host-id:size`")
	}
	cID, err := strconv.ParseUint(parts[0], 0, 16)
	if err != nil {
		return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid container id for user namespace remapping")
	}
	hID, err := strconv.ParseUint(parts[1], 0, 16)
	if err != nil {
		return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid host id for user namespace remapping")
	}
	size, err := strconv.ParseUint(parts[2], 0, 16)
	if err != nil {
		return specs.LinuxIDMapping{}, errors.Wrapf(err, "invalid size for user namespace remapping")
	}
	return specs.LinuxIDMapping{
		ContainerID: uint32(cID),
		HostID:      uint32(hID),
		Size:        uint32(size),
	}, nil
}

func validNamespace(ns string) bool {
	linuxNs := specs.LinuxNamespaceType(ns)
	switch linuxNs {
	case specs.PIDNamespace,
		specs.NetworkNamespace,
		specs.UTSNamespace,
		specs.MountNamespace,
		specs.UserNamespace,
		specs.IPCNamespace,
		specs.CgroupNamespace:
		return true
	default:
		return false
	}
}
<|endoftext|>