<commit_before>package emotechief

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"

	"github.com/gempir/gempbot/pkg/channelpoint"
	"github.com/gempir/gempbot/pkg/log"
	"github.com/gempir/gempbot/pkg/store"
	nickHelix "github.com/nicklaw5/helix/v2"
)

var sevenTvRegex = regexp.MustCompile(`https?:\/\/7tv.app\/emotes\/(\w*)`)

const sevenTvApiBaseUrl = "https://api.7tv.app/v2"

type GqlQuery struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type SevenTvUserResponse struct {
	Data struct {
		User struct {
			ID           string        `json:"id"`
			EmoteAliases []interface{} `json:"emote_aliases"`
			Emotes       []struct {
				ID         string `json:"id"`
				Name       string `json:"name"`
				Status     int    `json:"status"`
				Visibility int    `json:"visibility"`
				Width      []int  `json:"width"`
				Height     []int  `json:"height"`
			} `json:"emotes"`
			EmoteSlots int    `json:"emote_slots"`
			Banned     bool   `json:"banned"`
			YoutubeID  string `json:"youtube_id"`
		} `json:"user"`
	} `json:"data"`
}

func (e *EmoteChief) SetSevenTvEmote(channelUserID, emoteId, channel string, slots int) (addedEmote *sevenTvEmote, removedEmote *sevenTvEmote, err error) {
	emote, err := getSevenTvEmote(emoteId)
	if err != nil {
		return
	}

	gqlQuery := GqlQuery{
		Query: `
		query GetUser($id: String!) {
			user(id: $id) {
			  ...FullUser
			  banned
			  youtube_id
			}
		  }

		fragment FullUser on User {
			id
			emote_aliases
			emotes {
				id
				name
				status
				visibility
				width
				height
			}
			emote_slots
		}
		`,
		Variables: map[string]interface{}{"id": "60ae3e98b2ecb0150535c6b7"},
	}

	data, err := json.Marshal(gqlQuery)
	if err != nil {
		return
	}

	req, err := http.NewRequest(http.MethodPost, "https://api.7tv.app/v2/gql", bytes.NewBuffer(data))
	req.Header.Set("Content-Type", "application/json")
	// req.Header.Set("authorization", "Bearer "+e.cfg.SevenTvToken)
	if err != nil {
		return
	}

	resp, err := e.httpClient.Do(req)
	if err != nil {
		return
	}

	var userData SevenTvUserResponse
	err = json.NewDecoder(resp.Body).Decode(&userData)
	if err != nil {
		return
	}

	log.Info(userData.Data.User.Emotes)

	return emote, nil, nil
}

func (ec *EmoteChief) HandleSeventvRedemption(reward store.ChannelPointReward, redemption nickHelix.EventSubChannelPointsCustomRewardRedemptionEvent) {
	opts := channelpoint.UnmarshallSevenTvAdditionalOptions(reward.AdditionalOptions)
	success := false

	matches := sevenTvRegex.FindAllStringSubmatch(redemption.UserInput, -1)
	if len(matches) == 1 && len(matches[0]) == 2 {
		emoteAdded, emoteRemoved, err := ec.SetSevenTvEmote(redemption.BroadcasterUserID, matches[0][1], redemption.BroadcasterUserLogin, opts.Slots)
		if err != nil {
			log.Warnf("7tv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from: @%s error: %s", redemption.UserName, err.Error()))
		} else if emoteAdded != nil && emoteRemoved != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote: %s redeemed by @%s removed: %s", emoteAdded.Name, redemption.UserName, emoteRemoved.Name))
		} else if emoteAdded != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote: %s redeemed by @%s", emoteAdded.Name, redemption.UserName))
		} else {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote: [unknown] redeemed by @%s", redemption.UserName))
		}
	} else {
		ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s error: no 7tv link found in message", redemption.UserName))
	}

	token, err := ec.db.GetUserAccessToken(redemption.BroadcasterUserID)
	if err != nil {
		log.Errorf("Failed to get userAccess token to update redemption status for %s", redemption.BroadcasterUserID)
		return
	} else {
		err := ec.helixClient.UpdateRedemptionStatus(redemption.BroadcasterUserID, token.AccessToken, redemption.Reward.ID, redemption.ID, success)
		if err != nil {
			log.Errorf("Failed to update redemption status %s", err.Error())
			return
		}
	}

}

type sevenTvEmote struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Owner struct {
		ID          string `json:"id"`
		TwitchID    string `json:"twitch_id"`
		Login       string `json:"login"`
		DisplayName string `json:"display_name"`
		Role        struct {
			ID       string `json:"id"`
			Name     string `json:"name"`
			Position int    `json:"position"`
			Color    int    `json:"color"`
			Allowed  int    `json:"allowed"`
			Denied   int    `json:"denied"`
			Default  bool   `json:"default"`
		} `json:"role"`
	} `json:"owner"`
	Visibility       int           `json:"visibility"`
	VisibilitySimple []interface{} `json:"visibility_simple"`
	Mime             string        `json:"mime"`
	Status           int           `json:"status"`
	Tags             []interface{} `json:"tags"`
	Width            []int         `json:"width"`
	Height           []int         `json:"height"`
	Urls             [][]string    `json:"urls"`
}

func getSevenTvEmote(emoteID string) (*sevenTvEmote, error) {
	if emoteID == "" {
		return nil, nil
	}

	response, err := http.Get(sevenTvApiBaseUrl + "/emotes/" + emoteID)
	if err != nil {
		log.Error(err)
		return nil, err
	}

	if response.StatusCode <= 100 || response.StatusCode >= 400 {
		return nil, fmt.Errorf("Bad 7tv response: %d", response.StatusCode)
	}

	var emoteResponse sevenTvEmote
	err = json.NewDecoder(response.Body).Decode(&emoteResponse)
	if err != nil {
		return nil, err
	}

	return &emoteResponse, nil
}
<commit_msg>limit request size and determine current slots and max<commit_after>package emotechief

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"

	"github.com/gempir/gempbot/pkg/channelpoint"
	"github.com/gempir/gempbot/pkg/log"
	"github.com/gempir/gempbot/pkg/store"
	nickHelix "github.com/nicklaw5/helix/v2"
)

var sevenTvRegex = regexp.MustCompile(`https?:\/\/7tv.app\/emotes\/(\w*)`)

const sevenTvApiBaseUrl = "https://api.7tv.app/v2"

type GqlQuery struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type SevenTvUserResponse struct {
	Data struct {
		User struct {
			ID           string        `json:"id"`
			EmoteAliases []interface{} `json:"emote_aliases"`
			Emotes       []struct {
				ID         string `json:"id"`
				Name       string `json:"name"`
				Status     int    `json:"status"`
				Visibility int    `json:"visibility"`
				Width      []int  `json:"width"`
				Height     []int  `json:"height"`
			} `json:"emotes"`
			EmoteSlots int `json:"emote_slots"`
		} `json:"user"`
	} `json:"data"`
}

func (e *EmoteChief) SetSevenTvEmote(channelUserID, login, emoteId, channel string, slots int) (addedEmote *sevenTvEmote, removedEmote *sevenTvEmote, err error) {
	emote, err := getSevenTvEmote(emoteId)
	if err != nil {
		return
	}

	gqlQuery := GqlQuery{
		Query: `
		query GetUser($id: String!) {
			user(id: $id) {
			  ...FullUser
			}
		  }

		fragment FullUser on User {
			id
			emote_aliases
			emotes {
				id
				name
				status
				visibility
				width
				height
			}
			emote_slots
		}
		`,
		Variables: map[string]interface{}{"id": login},
	}

	data, err := json.Marshal(gqlQuery)
	if err != nil {
		return
	}

	req, err := http.NewRequest(http.MethodPost, "https://api.7tv.app/v2/gql", bytes.NewBuffer(data))
	req.Header.Set("Content-Type", "application/json")
	// req.Header.Set("authorization", "Bearer "+e.cfg.SevenTvToken)
	if err != nil {
		return
	}

	resp, err := e.httpClient.Do(req)
	if err != nil {
		return
	}

	var userData SevenTvUserResponse
	err = json.NewDecoder(resp.Body).Decode(&userData)
	if err != nil {
		return
	}

	log.Infof("%d/%d", len(userData.Data.User.Emotes), userData.Data.User.EmoteSlots)

	return emote, nil, nil
}

func (ec *EmoteChief) HandleSeventvRedemption(reward store.ChannelPointReward, redemption nickHelix.EventSubChannelPointsCustomRewardRedemptionEvent) {
	opts := channelpoint.UnmarshallSevenTvAdditionalOptions(reward.AdditionalOptions)
	success := false

	matches := sevenTvRegex.FindAllStringSubmatch(redemption.UserInput, -1)
	if len(matches) == 1 && len(matches[0]) == 2 {
		emoteAdded, emoteRemoved, err := ec.SetSevenTvEmote(redemption.BroadcasterUserID, redemption.BroadcasterUserLogin, matches[0][1], redemption.BroadcasterUserLogin, opts.Slots)
		if err != nil {
			log.Warnf("7tv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from: @%s error: %s", redemption.UserName, err.Error()))
		} else if emoteAdded != nil && emoteRemoved != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote: %s redeemed by @%s removed: %s", emoteAdded.Name, redemption.UserName, emoteRemoved.Name))
		} else if emoteAdded != nil {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote: %s redeemed by @%s", emoteAdded.Name, redemption.UserName))
		} else {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote: [unknown] redeemed by @%s", redemption.UserName))
		}
	} else {
		ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s error: no 7tv link found in message", redemption.UserName))
	}

	token, err := ec.db.GetUserAccessToken(redemption.BroadcasterUserID)
	if err != nil {
		log.Errorf("Failed to get userAccess token to update redemption status for %s", redemption.BroadcasterUserID)
		return
	} else {
		err := ec.helixClient.UpdateRedemptionStatus(redemption.BroadcasterUserID, token.AccessToken, redemption.Reward.ID, redemption.ID, success)
		if err != nil {
			log.Errorf("Failed to update redemption status %s", err.Error())
			return
		}
	}

}

type sevenTvEmote struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Owner struct {
		ID          string `json:"id"`
		TwitchID    string `json:"twitch_id"`
		Login       string `json:"login"`
		DisplayName string `json:"display_name"`
		Role        struct {
			ID       string `json:"id"`
			Name     string `json:"name"`
			Position int    `json:"position"`
			Color    int    `json:"color"`
			Allowed  int    `json:"allowed"`
			Denied   int    `json:"denied"`
			Default  bool   `json:"default"`
		} `json:"role"`
	} `json:"owner"`
	Visibility       int           `json:"visibility"`
	VisibilitySimple []interface{} `json:"visibility_simple"`
	Mime             string        `json:"mime"`
	Status           int           `json:"status"`
	Tags             []interface{} `json:"tags"`
	Width            []int         `json:"width"`
	Height           []int         `json:"height"`
	Urls             [][]string    `json:"urls"`
}

func getSevenTvEmote(emoteID string) (*sevenTvEmote, error) {
	if emoteID == "" {
		return nil, nil
	}

	response, err := http.Get(sevenTvApiBaseUrl + "/emotes/" + emoteID)
	if err != nil {
		log.Error(err)
		return nil, err
	}

	if response.StatusCode <= 100 || response.StatusCode >= 400 {
		return nil, fmt.Errorf("Bad 7tv response: %d", response.StatusCode)
	}

	var emoteResponse sevenTvEmote
	err = json.NewDecoder(response.Body).Decode(&emoteResponse)
	if err != nil {
		return nil, err
	}

	return &emoteResponse, nil
}
<|endoftext|>


<commit_before>package engine

import (
	"capsulecd/pkg/config"
	"capsulecd/pkg/errors"
	"fmt"
	"github.com/Masterminds/semver"
	"capsulecd/pkg/utils"
	"path"
	"capsulecd/pkg/pipeline"
)

type engineBase struct {
	Config config.Interface
	PipelineData    *pipeline.Data
}

// default Compile Step.
func (g *engineBase) CompileStep() error {
	if !g.Config.GetBool("engine_disable_compile") {
		if terr := g.ExecuteCmdList("engine_cmd_compile",
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Compile command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}
	return nil
}

// default Test step
// assumes that the lint and code fmt commands are very similar and that engine_cmd_fmt includes engine_cmd_lint.
func (g *engineBase) TestStep() error {

	//skip the lint commands if disabled
	if !g.Config.GetBool("engine_disable_lint") {
		//run test command
		lintKey := "engine_cmd_lint"
		if g.Config.GetBool("engine_enable_code_mutation") {
			lintKey = "engine_cmd_fmt"
		}

		if terr := g.ExecuteCmdList(lintKey,
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Lint command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}

	//skip the test commands if disabled
	if !g.Config.GetBool("engine_disable_test") {
		//run test command
		if terr := g.ExecuteCmdList("engine_cmd_test",
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Test command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}

	//skip the security test commands if disabled
	if !g.Config.GetBool("engine_disable_security_check") {
		//run security check command
		if terr := g.ExecuteCmdList("engine_cmd_security_check",
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Dependency vulnerability check command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}

	return nil
}

//Helper functions

func (e *engineBase) BumpVersion(currentVersion string) (string, error) {
	v, nerr := semver.NewVersion(currentVersion)
	if nerr != nil {
		return "", nerr
	}

	switch bumpType := e.Config.GetString("engine_version_bump_type"); bumpType {
	case "major":
		return fmt.Sprintf("%d.%d.%d", v.Major()+1, 0, 0), nil
	case "minor":
		return fmt.Sprintf("%d.%d.%d", v.Major(), v.Minor()+1, 0), nil
	case "patch":
		return fmt.Sprintf("%d.%d.%d", v.Major(), v.Minor(), v.Patch()+1), nil
	default:
		return "", errors.Custom("Unknown version bump interval")
	}

}

func (e *engineBase) ExecuteCmdList(configKey string, workingDir string, environ []string, logPrefix string, errorTemplate string) error {
	cmd := e.Config.GetString(configKey)

	// we have to support 2 types of cmds.
	// - simple commands (engine_cmd_compile: 'compile command')
	// and list commands (engine_cmd_compile: - 'compile command' \n - 'compile command 2' \n ..)
	// GetString will return "" if this is a list of commands.

	if(cmd != ""){
		//code formatter
		if terr := utils.BashCmdExec(cmd, workingDir, environ, logPrefix); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf(errorTemplate, cmd))
		}
	} else {
		cmdList := e.Config.GetStringSlice(configKey)
		if cmdList == nil {
			return nil
		}
		for cmdEntry := range cmdList {
			if terr := utils.BashCmdExec(cmdEntry, workingDir, environ, logPrefix); terr != nil {
				return errors.EngineTestRunnerError(fmt.Sprintf(errorTemplate, cmdEntry))
			}
		}
	}
	return nil
}<commit_msg>fixing engine base.<commit_after>package engine

import (
	"capsulecd/pkg/config"
	"capsulecd/pkg/errors"
	"fmt"
	"github.com/Masterminds/semver"
	"capsulecd/pkg/utils"
	"capsulecd/pkg/pipeline"
)

type engineBase struct {
	Config config.Interface
	PipelineData    *pipeline.Data
}

// default Compile Step.
func (g *engineBase) CompileStep() error {
	if !g.Config.GetBool("engine_disable_compile") {
		if terr := g.ExecuteCmdList("engine_cmd_compile",
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Compile command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}
	return nil
}

// default Test step
// assumes that the lint and code fmt commands are very similar and that engine_cmd_fmt includes engine_cmd_lint.
func (g *engineBase) TestStep() error {

	//skip the lint commands if disabled
	if !g.Config.GetBool("engine_disable_lint") {
		//run test command
		lintKey := "engine_cmd_lint"
		if g.Config.GetBool("engine_enable_code_mutation") {
			lintKey = "engine_cmd_fmt"
		}

		if terr := g.ExecuteCmdList(lintKey,
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Lint command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}

	//skip the test commands if disabled
	if !g.Config.GetBool("engine_disable_test") {
		//run test command
		if terr := g.ExecuteCmdList("engine_cmd_test",
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Test command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}

	//skip the security test commands if disabled
	if !g.Config.GetBool("engine_disable_security_check") {
		//run security check command
		if terr := g.ExecuteCmdList("engine_cmd_security_check",
			g.PipelineData.GitLocalPath,
			nil,
			"",
			"Dependency vulnerability check command (%s) failed. Check log for more details.",
		); terr != nil {
			return terr
		}
	}

	return nil
}

//Helper functions

func (e *engineBase) BumpVersion(currentVersion string) (string, error) {
	v, nerr := semver.NewVersion(currentVersion)
	if nerr != nil {
		return "", nerr
	}

	switch bumpType := e.Config.GetString("engine_version_bump_type"); bumpType {
	case "major":
		return fmt.Sprintf("%d.%d.%d", v.Major()+1, 0, 0), nil
	case "minor":
		return fmt.Sprintf("%d.%d.%d", v.Major(), v.Minor()+1, 0), nil
	case "patch":
		return fmt.Sprintf("%d.%d.%d", v.Major(), v.Minor(), v.Patch()+1), nil
	default:
		return "", errors.Custom("Unknown version bump interval")
	}

}

func (e *engineBase) ExecuteCmdList(configKey string, workingDir string, environ []string, logPrefix string, errorTemplate string) error {
	cmd := e.Config.GetString(configKey)

	// we have to support 2 types of cmds.
	// - simple commands (engine_cmd_compile: 'compile command')
	// and list commands (engine_cmd_compile: - 'compile command' \n - 'compile command 2' \n ..)
	// GetString will return "" if this is a list of commands.

	if(cmd != ""){
		//code formatter
		if terr := utils.BashCmdExec(cmd, workingDir, environ, logPrefix); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf(errorTemplate, cmd))
		}
	} else {
		cmdList := e.Config.GetStringSlice(configKey)
		if cmdList == nil {
			return nil
		}
		for i := range cmdList {
			if terr := utils.BashCmdExec(cmdList[i], workingDir, environ, logPrefix); terr != nil {
				return errors.EngineTestRunnerError(fmt.Sprintf(errorTemplate, cmdList[i]))
			}
		}
	}
	return nil
}<|endoftext|>


<commit_before>package handlers

import (
	"github.com/gilcrest/go-API-template/pkg/config/env"
	"github.com/gorilla/mux"
)

// PathMatch is a way of organizing routing to handlers (versioning as well)
func PathMatch(env *env.Env, rtr *mux.Router) *mux.Router {

	// match only POST requests on /api/appUser/create
	// This is the original (v1) version for the API and the response for this will never change
	//  with versioning in order to maintain a stable contract
	// func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, *http.Request)) *Route
	rtr.Handle("/api/appUser/create", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	// match only POST requests on /api/v1/appUser/create
	// func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, *http.Request)) *Route
	//rtr.HandleFunc("/api/v1/appUser/create", createUserHandler).
	//	Methods("POST").
	//	Headers("Content-Type", "application/json")

	return rtr
}
<commit_msg>Added v1 back in<commit_after>package handlers

import (
	"github.com/gilcrest/go-API-template/pkg/config/env"
	"github.com/gorilla/mux"
)

// PathMatch is a way of organizing routing to handlers (versioning as well)
func PathMatch(env *env.Env, rtr *mux.Router) *mux.Router {

	// match only POST requests on /api/appUser/create
	// This is the original (v1) version for the API and the response for this will never change
	//  with versioning in order to maintain a stable contract
	// func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, *http.Request)) *Route
	rtr.Handle("/api/appUser/create", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	// match only POST requests on /api/v1/appUser/create
	// func(path string, handler http.Handler) *Route
	rtr.Handle("/api/v1/appUser/create", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	return rtr
}
<|endoftext|>


<commit_before>package jobs

import (
	"context"
	"math/rand"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func randomMicro(min, max int) time.Duration {
	return time.Duration(rand.Intn(max-min)+min) * time.Microsecond
}

func TestInMemoryJobs(t *testing.T) {
	n := 1000
	v := 100

	var w sync.WaitGroup

	var workersTestList = WorkersList{
		"test": {
			Concurrency: 4,
			WorkerFunc: func(ctx context.Context, m *Message) error {
				var msg string
				err := m.Unmarshal(&msg)
				if !assert.NoError(t, err) {
					return err
				}
				if strings.HasPrefix(msg, "a-") {
					_, err := strconv.Atoi(msg[len("a-"):])
					assert.NoError(t, err)
				} else if strings.HasPrefix(msg, "b-") {
					_, err := strconv.Atoi(msg[len("b-"):])
					assert.NoError(t, err)
				} else {
					t.Fatal()
				}
				w.Done()
				return nil
			},
		},
	}

	w.Add(2)

	go func() {
		broker := NewMemBroker("cozy.local", workersTestList)
		for i := 0; i < n; i++ {
			w.Add(1)
			msg, _ := NewMessage(JSONEncoding, "a-"+strconv.Itoa(i+1))
			_, _, err := broker.PushJob(&JobRequest{
				WorkerType: "test",
				Message:    msg,
			})
			assert.NoError(t, err)
			time.Sleep(randomMicro(0, v))
		}
		w.Done()
	}()

	go func() {
		broker := NewMemBroker("cozy.local", workersTestList)
		for i := 0; i < n; i++ {
			w.Add(1)
			msg, _ := NewMessage(JSONEncoding, "b-"+strconv.Itoa(i+1))
			_, _, err := broker.PushJob(&JobRequest{
				WorkerType: "test",
				Message:    msg,
			})
			assert.NoError(t, err)
			time.Sleep(randomMicro(0, v))
		}
		w.Done()
	}()

	w.Wait()
}

func TestUnknownWorkerError(t *testing.T) {
	broker := NewMemBroker("baz.quz", WorkersList{})
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "nope",
		Message:    nil,
	})
	assert.Error(t, err)
	assert.Equal(t, ErrUnknownWorker, err)
}

func TestUnknownMessageType(t *testing.T) {
	var w sync.WaitGroup

	broker := NewMemBroker("foo.bar", WorkersList{
		"test": {
			Concurrency: 4,
			WorkerFunc: func(ctx context.Context, m *Message) error {
				var msg string
				err := m.Unmarshal(&msg)
				assert.Error(t, err)
				assert.Equal(t, ErrUnknownMessageType, err)
				w.Done()
				return nil
			},
		},
	})

	w.Add(1)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "test",
		Message: &Message{
			Type: "unknown",
			Data: nil,
		},
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestTimeout(t *testing.T) {
	var w sync.WaitGroup

	broker := NewMemBroker("timeout.cozy", WorkersList{
		"timeout": {
			Concurrency:  1,
			MaxExecCount: 1,
			Timeout:      1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				<-ctx.Done()
				w.Done()
				return ctx.Err()
			},
		},
	})

	w.Add(1)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "timeout",
		Message: &Message{
			Type: "timeout",
			Data: nil,
		},
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestRetry(t *testing.T) {
	var w sync.WaitGroup

	maxExecCount := 4

	var count int
	broker := NewMemBroker("retry", WorkersList{
		"test": {
			Concurrency:  1,
			MaxExecCount: uint(maxExecCount),
			Timeout:      1 * time.Millisecond,
			RetryDelay:   1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				<-ctx.Done()
				w.Done()
				count++
				if count < maxExecCount {
					return ctx.Err()
				}
				return nil
			},
		},
	})

	w.Add(maxExecCount)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "test",
		Message:    nil,
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestPanicRetried(t *testing.T) {
	var w sync.WaitGroup

	maxExecCount := 4

	broker := NewMemBroker("panic", WorkersList{
		"panic": {
			Concurrency:  1,
			MaxExecCount: uint(maxExecCount),
			RetryDelay:   1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				w.Done()
				panic("oops")
			},
		},
	})

	w.Add(maxExecCount)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "panic",
		Message:    nil,
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestPanic(t *testing.T) {
	var w sync.WaitGroup

	even, _ := NewMessage("json", 0)
	odd, _ := NewMessage("json", 1)

	broker := NewMemBroker("panic2", WorkersList{
		"panic2": {
			Concurrency:  1,
			MaxExecCount: 1,
			RetryDelay:   1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, m *Message) error {
				var i int
				if err := m.Unmarshal(&i); err != nil {
					return err
				}
				if i%2 != 0 {
					panic("oops")
				}
				w.Done()
				return nil
			},
		},
	})
	w.Add(2)
	var err error
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: odd})
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: even})
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: odd})
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: even})
	assert.NoError(t, err)
	w.Wait()
}

func TestInfoChan(t *testing.T) {
	var w sync.WaitGroup

	broker := NewMemBroker("chan.cozy", WorkersList{
		"timeout": {
			Concurrency:  1,
			MaxExecCount: 1,
			Timeout:      1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				<-ctx.Done()
				w.Done()
				return ctx.Err()
			},
		},
	})

	w.Add(1)
	job, done, err := broker.PushJob(&JobRequest{
		WorkerType: "timeout",
		Message: &Message{
			Type: "timeout",
			Data: nil,
		},
	})

	assert.Equal(t, Queued, job.State)

	job = <-done
	assert.Equal(t, string(Running), string(job.State))

	job = <-done
	assert.Equal(t, string(Errored), string(job.State))

	job = <-done
	assert.Nil(t, job)

	assert.NoError(t, err)
	w.Wait()
}
<commit_msg>Add assert noerror<commit_after>package jobs

import (
	"context"
	"math/rand"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func randomMicro(min, max int) time.Duration {
	return time.Duration(rand.Intn(max-min)+min) * time.Microsecond
}

func TestInMemoryJobs(t *testing.T) {
	n := 1000
	v := 100

	var w sync.WaitGroup

	var workersTestList = WorkersList{
		"test": {
			Concurrency: 4,
			WorkerFunc: func(ctx context.Context, m *Message) error {
				var msg string
				err := m.Unmarshal(&msg)
				if !assert.NoError(t, err) {
					return err
				}
				if strings.HasPrefix(msg, "a-") {
					_, err := strconv.Atoi(msg[len("a-"):])
					assert.NoError(t, err)
				} else if strings.HasPrefix(msg, "b-") {
					_, err := strconv.Atoi(msg[len("b-"):])
					assert.NoError(t, err)
				} else {
					t.Fatal()
				}
				w.Done()
				return nil
			},
		},
	}

	w.Add(2)

	go func() {
		broker := NewMemBroker("cozy.local", workersTestList)
		for i := 0; i < n; i++ {
			w.Add(1)
			msg, _ := NewMessage(JSONEncoding, "a-"+strconv.Itoa(i+1))
			_, _, err := broker.PushJob(&JobRequest{
				WorkerType: "test",
				Message:    msg,
			})
			assert.NoError(t, err)
			time.Sleep(randomMicro(0, v))
		}
		w.Done()
	}()

	go func() {
		broker := NewMemBroker("cozy.local", workersTestList)
		for i := 0; i < n; i++ {
			w.Add(1)
			msg, _ := NewMessage(JSONEncoding, "b-"+strconv.Itoa(i+1))
			_, _, err := broker.PushJob(&JobRequest{
				WorkerType: "test",
				Message:    msg,
			})
			assert.NoError(t, err)
			time.Sleep(randomMicro(0, v))
		}
		w.Done()
	}()

	w.Wait()
}

func TestUnknownWorkerError(t *testing.T) {
	broker := NewMemBroker("baz.quz", WorkersList{})
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "nope",
		Message:    nil,
	})
	assert.Error(t, err)
	assert.Equal(t, ErrUnknownWorker, err)
}

func TestUnknownMessageType(t *testing.T) {
	var w sync.WaitGroup

	broker := NewMemBroker("foo.bar", WorkersList{
		"test": {
			Concurrency: 4,
			WorkerFunc: func(ctx context.Context, m *Message) error {
				var msg string
				err := m.Unmarshal(&msg)
				assert.Error(t, err)
				assert.Equal(t, ErrUnknownMessageType, err)
				w.Done()
				return nil
			},
		},
	})

	w.Add(1)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "test",
		Message: &Message{
			Type: "unknown",
			Data: nil,
		},
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestTimeout(t *testing.T) {
	var w sync.WaitGroup

	broker := NewMemBroker("timeout.cozy", WorkersList{
		"timeout": {
			Concurrency:  1,
			MaxExecCount: 1,
			Timeout:      1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				<-ctx.Done()
				w.Done()
				return ctx.Err()
			},
		},
	})

	w.Add(1)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "timeout",
		Message: &Message{
			Type: "timeout",
			Data: nil,
		},
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestRetry(t *testing.T) {
	var w sync.WaitGroup

	maxExecCount := 4

	var count int
	broker := NewMemBroker("retry", WorkersList{
		"test": {
			Concurrency:  1,
			MaxExecCount: uint(maxExecCount),
			Timeout:      1 * time.Millisecond,
			RetryDelay:   1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				<-ctx.Done()
				w.Done()
				count++
				if count < maxExecCount {
					return ctx.Err()
				}
				return nil
			},
		},
	})

	w.Add(maxExecCount)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "test",
		Message:    nil,
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestPanicRetried(t *testing.T) {
	var w sync.WaitGroup

	maxExecCount := 4

	broker := NewMemBroker("panic", WorkersList{
		"panic": {
			Concurrency:  1,
			MaxExecCount: uint(maxExecCount),
			RetryDelay:   1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				w.Done()
				panic("oops")
			},
		},
	})

	w.Add(maxExecCount)
	_, _, err := broker.PushJob(&JobRequest{
		WorkerType: "panic",
		Message:    nil,
	})

	assert.NoError(t, err)
	w.Wait()
}

func TestPanic(t *testing.T) {
	var w sync.WaitGroup

	even, _ := NewMessage("json", 0)
	odd, _ := NewMessage("json", 1)

	broker := NewMemBroker("panic2", WorkersList{
		"panic2": {
			Concurrency:  1,
			MaxExecCount: 1,
			RetryDelay:   1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, m *Message) error {
				var i int
				if err := m.Unmarshal(&i); err != nil {
					return err
				}
				if i%2 != 0 {
					panic("oops")
				}
				w.Done()
				return nil
			},
		},
	})
	w.Add(2)
	var err error
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: odd})
	assert.NoError(t, err)
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: even})
	assert.NoError(t, err)
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: odd})
	assert.NoError(t, err)
	_, _, err = broker.PushJob(&JobRequest{WorkerType: "panic2", Message: even})
	assert.NoError(t, err)
	w.Wait()
}

func TestInfoChan(t *testing.T) {
	var w sync.WaitGroup

	broker := NewMemBroker("chan.cozy", WorkersList{
		"timeout": {
			Concurrency:  1,
			MaxExecCount: 1,
			Timeout:      1 * time.Millisecond,
			WorkerFunc: func(ctx context.Context, _ *Message) error {
				<-ctx.Done()
				w.Done()
				return ctx.Err()
			},
		},
	})

	w.Add(1)
	job, done, err := broker.PushJob(&JobRequest{
		WorkerType: "timeout",
		Message: &Message{
			Type: "timeout",
			Data: nil,
		},
	})

	assert.Equal(t, Queued, job.State)

	job = <-done
	assert.Equal(t, string(Running), string(job.State))

	job = <-done
	assert.Equal(t, string(Errored), string(job.State))

	job = <-done
	assert.Nil(t, job)

	assert.NoError(t, err)
	w.Wait()
}
<|endoftext|>


<commit_before>/*
Copyright The Helm 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 kube // import "helm.sh/helm/v3/pkg/kube"

import (
	"testing"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/resource"
)

func TestResourceList(t *testing.T) {
	mapping := &meta.RESTMapping{
		Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "pod"},
	}

	info := func(name string) *resource.Info {
		return &resource.Info{Name: name, Mapping: mapping}
	}

	var r1, r2 ResourceList
	r1 = []*resource.Info{info("foo"), info("bar")}
	r2 = []*resource.Info{info("bar")}

	diff := r1.Difference(r2)
	if len(diff) != 1 {
		t.Error("expected 1 result")
	}

	if !diff.Contains(info("foo")) {
		t.Error("expected diff to return foo")
	}

	inter := r1.Intersect(r2)
	if len(inter) != 1 {
		t.Error("expected 1 result")
	}

	if !inter.Contains(info("bar")) {
		t.Error("expected intersect to return bar")
	}
}
<commit_msg>Add corresponding unit test to the function in resource.go.<commit_after>/*
Copyright The Helm 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 kube // import "helm.sh/helm/v3/pkg/kube"

import (
	"testing"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/resource"
)

func TestResourceList(t *testing.T) {
	mapping := &meta.RESTMapping{
		Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "pod"},
	}

	info := func(name string) *resource.Info {
		return &resource.Info{Name: name, Mapping: mapping}
	}

	var r1, r2 ResourceList
	r1 = []*resource.Info{info("foo"), info("bar")}
	r2 = []*resource.Info{info("bar")}

	if r1.Get(info("bar")).Mapping.Resource.Resource != "pod" {
		t.Error("expected get pod")
	}

	diff := r1.Difference(r2)
	if len(diff) != 1 {
		t.Error("expected 1 result")
	}

	if !diff.Contains(info("foo")) {
		t.Error("expected diff to return foo")
	}

	inter := r1.Intersect(r2)
	if len(inter) != 1 {
		t.Error("expected 1 result")
	}

	if !inter.Contains(info("bar")) {
		t.Error("expected intersect to return bar")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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 cmd

import (
	"fmt"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/cobra"

	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/dynamic"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

var (
	delete_long = templates.LongDesc(i18n.T(`
		Delete resources by filenames, stdin, resources and names, or by resources and label selector.

		JSON and YAML formats are accepted. Only one type of the arguments may be specified: filenames,
		resources and names, or resources and label selector.

		Some resources, such as pods, support graceful deletion. These resources define a default period
		before they are forcibly terminated (the grace period) but you may override that value with
		the --grace-period flag, or pass --now to set a grace-period of 1. Because these resources often
		represent entities in the cluster, deletion may not be acknowledged immediately. If the node
		hosting a pod is down or cannot reach the API server, termination may take significantly longer
		than the grace period. To force delete a resource, you must pass a grace period of 0 and specify
		the --force flag.

		IMPORTANT: Force deleting pods does not wait for confirmation that the pod's processes have been
		terminated, which can leave those processes running until the node detects the deletion and
		completes graceful deletion. If your processes use shared storage or talk to a remote API and
		depend on the name of the pod to identify themselves, force deleting those pods may result in
		multiple processes running on different machines using the same identification which may lead
		to data corruption or inconsistency. Only force delete pods when you are sure the pod is
		terminated, or if your application can tolerate multiple copies of the same pod running at once.
		Also, if you force delete pods the scheduler may place new pods on those nodes before the node
		has released those resources and causing those pods to be evicted immediately.

		Note that the delete command does NOT do resource version checks, so if someone submits an
		update to a resource right when you submit a delete, their update will be lost along with the
		rest of the resource.`))

	delete_example = templates.Examples(i18n.T(`
		# Delete a pod using the type and name specified in pod.json.
		kubectl delete -f ./pod.json

		# Delete a pod based on the type and name in the JSON passed into stdin.
		cat pod.json | kubectl delete -f -

		# Delete pods and services with same names "baz" and "foo"
		kubectl delete pod,service baz foo

		# Delete pods and services with label name=myLabel.
		kubectl delete pods,services -l name=myLabel

		# Delete a pod with minimal delay
		kubectl delete pod foo --now

		# Force delete a pod on a dead node
		kubectl delete pod foo --grace-period=0 --force

		# Delete all pods
		kubectl delete pods --all`))
)

type DeleteOptions struct {
	resource.FilenameOptions

	LabelSelector   string
	FieldSelector   string
	DeleteAll       bool
	IgnoreNotFound  bool
	Cascade         bool
	DeleteNow       bool
	ForceDeletion   bool
	WaitForDeletion bool

	GracePeriod int
	Timeout     time.Duration

	Output string

	DynamicClient dynamic.Interface
	Mapper        meta.RESTMapper
	Result        *resource.Result

	genericclioptions.IOStreams
}

func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")

	cmd := &cobra.Command{
		Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
		DisableFlagsInUseLine: true,
		Short:   i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"),
		Long:    delete_long,
		Example: delete_example,
		Run: func(cmd *cobra.Command, args []string) {
			o := deleteFlags.ToOptions(nil, streams)
			cmdutil.CheckErr(o.Complete(f, args, cmd))
			cmdutil.CheckErr(o.Validate(cmd))
			cmdutil.CheckErr(o.RunDelete())
		},
		SuggestFor: []string{"rm"},
	}

	deleteFlags.AddFlags(cmd)

	cmdutil.AddIncludeUninitializedFlag(cmd)
	return cmd
}

func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	if o.DeleteAll || len(o.LabelSelector) > 0 || len(o.FieldSelector) > 0 {
		if f := cmd.Flags().Lookup("ignore-not-found"); f != nil && !f.Changed {
			// If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all, -l, or --field-selector
			o.IgnoreNotFound = true
		}
	}
	if o.DeleteNow {
		if o.GracePeriod != -1 {
			return fmt.Errorf("--now and --grace-period cannot be specified together")
		}
		o.GracePeriod = 1
	}
	if o.GracePeriod == 0 && !o.ForceDeletion {
		// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
		// into --grace-period=1. Users may provide --force to bypass this conversion.
		o.GracePeriod = 1
	}

	includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
	r := f.NewBuilder().
		Unstructured().
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, &o.FilenameOptions).
		LabelSelectorParam(o.LabelSelector).
		FieldSelectorParam(o.FieldSelector).
		IncludeUninitialized(includeUninitialized).
		SelectAllParam(o.DeleteAll).
		ResourceTypeOrNameArgs(false, args...).RequireObject(false).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}
	o.Result = r

	o.Mapper, err = f.ToRESTMapper()
	if err != nil {
		return err
	}

	o.DynamicClient, err = f.DynamicClient()
	if err != nil {
		return err
	}

	return nil
}

func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
	if o.Output != "" && o.Output != "name" {
		return cmdutil.UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", o.Output)
	}

	if o.DeleteAll && len(o.LabelSelector) > 0 {
		return fmt.Errorf("cannot set --all and --selector at the same time")
	}
	if o.DeleteAll && len(o.FieldSelector) > 0 {
		return fmt.Errorf("cannot set --all and --field-selector at the same time")
	}

	if o.GracePeriod == 0 && !o.ForceDeletion && !o.WaitForDeletion {
		// With the explicit --wait flag we need extra validation for backward compatibility
		return fmt.Errorf("--grace-period=0 must have either --force specified, or --wait to be set to true")
	}

	switch {
	case o.GracePeriod == 0 && o.ForceDeletion:
		fmt.Fprintf(o.ErrOut, "warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.\n")
	case o.ForceDeletion:
		fmt.Fprintf(o.ErrOut, "warning: --force is ignored because --grace-period is not 0.\n")
	}
	return nil
}

func (o *DeleteOptions) RunDelete() error {
	return o.DeleteResult(o.Result)
}

func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
	found := 0
	if o.IgnoreNotFound {
		r = r.IgnoreErrors(errors.IsNotFound)
	}
	err := r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}
		found++

		options := &metav1.DeleteOptions{}
		if o.GracePeriod >= 0 {
			options = metav1.NewDeleteOptions(int64(o.GracePeriod))
		}
		policy := metav1.DeletePropagationForeground
		if !o.Cascade {
			policy = metav1.DeletePropagationOrphan
		}
		options.PropagationPolicy = &policy
		return o.deleteResource(info, options)
	})
	if err != nil {
		return err
	}
	if found == 0 {
		fmt.Fprintf(o.Out, "No resources found\n")
		return nil
	}
	if !o.WaitForDeletion {
		return nil
	}
	// if we don't have a dynamic client, we don't want to wait.  Eventually when delete is cleaned up, this will likely
	// drop out.
	if o.DynamicClient == nil {
		return nil
	}

	effectiveTimeout := o.Timeout
	if effectiveTimeout == 0 {
		// if we requested to wait forever, set it to a week.
		effectiveTimeout = 168 * time.Hour
	}
	waitOptions := kubectlwait.WaitOptions{
		ResourceFinder: genericclioptions.ResourceFinderForResult(r),
		DynamicClient:  o.DynamicClient,
		Timeout:        effectiveTimeout,

		Printer:     printers.NewDiscardingPrinter(),
		ConditionFn: kubectlwait.IsDeleted,
		IOStreams:   o.IOStreams,
	}
	err = waitOptions.RunWait()
	if errors.IsForbidden(err) {
		// if we're forbidden from waiting, we shouldn't fail.
		glog.V(1).Info(err)
		return nil
	}
	return err
}

func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) error {
	// TODO: Remove this in or after 1.12 release.
	//       Server version >= 1.11 no longer needs this hack.
	mapping := info.ResourceMapping()
	if mapping.Resource.GroupResource() == (schema.GroupResource{Group: "extensions", Resource: "daemonsets"}) ||
		mapping.Resource.GroupResource() == (schema.GroupResource{Group: "apps", Resource: "daemonsets"}) {
		if err := updateDaemonSet(info.Namespace, info.Name, o.DynamicClient); err != nil {
			return err
		}
	}

	if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil {
		return cmdutil.AddSourceToErr("deleting", info.Source, err)
	}

	o.PrintObj(info)
	return nil
}

// TODO: Remove this in or after 1.12 release.
//       Server version >= 1.11 no longer needs this hack.
func updateDaemonSet(namespace, name string, dynamicClient dynamic.Interface) error {
	dsClient := dynamicClient.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"}).Namespace(namespace)
	obj, err := dsClient.Get(name, metav1.GetOptions{})
	if err != nil {
		return err
	}
	ds := &appsv1.DaemonSet{}
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, ds); err != nil {
		return err
	}

	// We set the nodeSelector to a random label. This label is nearly guaranteed
	// to not be set on any node so the DameonSetController will start deleting
	// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
	// the DaemonSet.
	ds.Spec.Template.Spec.NodeSelector = map[string]string{
		string(uuid.NewUUID()): string(uuid.NewUUID()),
	}
	// force update to avoid version conflict
	ds.ResourceVersion = ""

	out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ds)
	if err != nil {
		return err
	}
	if _, err = dsClient.Update(&unstructured.Unstructured{Object: out}); err != nil {
		return err
	}

	// Wait for the daemon set controller to kill all the daemon pods.
	if err := wait.Poll(1*time.Second, 5*time.Minute, func() (bool, error) {
		updatedObj, err := dsClient.Get(name, metav1.GetOptions{})
		if err != nil {
			return false, nil
		}
		updatedDS := &appsv1.DaemonSet{}
		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(updatedObj.Object, ds); err != nil {
			return false, nil
		}

		return updatedDS.Status.CurrentNumberScheduled+updatedDS.Status.NumberMisscheduled == 0, nil
	}); err != nil {
		return err
	}
	return nil
}

// deletion printing is special because we do not have an object to print.
// This mirrors name printer behavior
func (o *DeleteOptions) PrintObj(info *resource.Info) {
	operation := "deleted"
	groupKind := info.Mapping.GroupVersionKind
	kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
	if len(groupKind.Group) == 0 {
		kindString = strings.ToLower(groupKind.Kind)
	}

	if o.GracePeriod == 0 {
		operation = "force deleted"
	}

	if o.Output == "name" {
		// -o name: prints resource/name
		fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name)
		return
	}

	// understandable output by default
	fmt.Fprintf(o.Out, "%s \"%s\" %s\n", kindString, info.Name, operation)
}
<commit_msg>delete should tolerate a failed wait because of missing verbs<commit_after>/*
Copyright 2014 The Kubernetes 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 cmd

import (
	"fmt"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/cobra"

	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/dynamic"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

var (
	delete_long = templates.LongDesc(i18n.T(`
		Delete resources by filenames, stdin, resources and names, or by resources and label selector.

		JSON and YAML formats are accepted. Only one type of the arguments may be specified: filenames,
		resources and names, or resources and label selector.

		Some resources, such as pods, support graceful deletion. These resources define a default period
		before they are forcibly terminated (the grace period) but you may override that value with
		the --grace-period flag, or pass --now to set a grace-period of 1. Because these resources often
		represent entities in the cluster, deletion may not be acknowledged immediately. If the node
		hosting a pod is down or cannot reach the API server, termination may take significantly longer
		than the grace period. To force delete a resource, you must pass a grace period of 0 and specify
		the --force flag.

		IMPORTANT: Force deleting pods does not wait for confirmation that the pod's processes have been
		terminated, which can leave those processes running until the node detects the deletion and
		completes graceful deletion. If your processes use shared storage or talk to a remote API and
		depend on the name of the pod to identify themselves, force deleting those pods may result in
		multiple processes running on different machines using the same identification which may lead
		to data corruption or inconsistency. Only force delete pods when you are sure the pod is
		terminated, or if your application can tolerate multiple copies of the same pod running at once.
		Also, if you force delete pods the scheduler may place new pods on those nodes before the node
		has released those resources and causing those pods to be evicted immediately.

		Note that the delete command does NOT do resource version checks, so if someone submits an
		update to a resource right when you submit a delete, their update will be lost along with the
		rest of the resource.`))

	delete_example = templates.Examples(i18n.T(`
		# Delete a pod using the type and name specified in pod.json.
		kubectl delete -f ./pod.json

		# Delete a pod based on the type and name in the JSON passed into stdin.
		cat pod.json | kubectl delete -f -

		# Delete pods and services with same names "baz" and "foo"
		kubectl delete pod,service baz foo

		# Delete pods and services with label name=myLabel.
		kubectl delete pods,services -l name=myLabel

		# Delete a pod with minimal delay
		kubectl delete pod foo --now

		# Force delete a pod on a dead node
		kubectl delete pod foo --grace-period=0 --force

		# Delete all pods
		kubectl delete pods --all`))
)

type DeleteOptions struct {
	resource.FilenameOptions

	LabelSelector   string
	FieldSelector   string
	DeleteAll       bool
	IgnoreNotFound  bool
	Cascade         bool
	DeleteNow       bool
	ForceDeletion   bool
	WaitForDeletion bool

	GracePeriod int
	Timeout     time.Duration

	Output string

	DynamicClient dynamic.Interface
	Mapper        meta.RESTMapper
	Result        *resource.Result

	genericclioptions.IOStreams
}

func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")

	cmd := &cobra.Command{
		Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
		DisableFlagsInUseLine: true,
		Short:   i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"),
		Long:    delete_long,
		Example: delete_example,
		Run: func(cmd *cobra.Command, args []string) {
			o := deleteFlags.ToOptions(nil, streams)
			cmdutil.CheckErr(o.Complete(f, args, cmd))
			cmdutil.CheckErr(o.Validate(cmd))
			cmdutil.CheckErr(o.RunDelete())
		},
		SuggestFor: []string{"rm"},
	}

	deleteFlags.AddFlags(cmd)

	cmdutil.AddIncludeUninitializedFlag(cmd)
	return cmd
}

func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	if o.DeleteAll || len(o.LabelSelector) > 0 || len(o.FieldSelector) > 0 {
		if f := cmd.Flags().Lookup("ignore-not-found"); f != nil && !f.Changed {
			// If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all, -l, or --field-selector
			o.IgnoreNotFound = true
		}
	}
	if o.DeleteNow {
		if o.GracePeriod != -1 {
			return fmt.Errorf("--now and --grace-period cannot be specified together")
		}
		o.GracePeriod = 1
	}
	if o.GracePeriod == 0 && !o.ForceDeletion {
		// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
		// into --grace-period=1. Users may provide --force to bypass this conversion.
		o.GracePeriod = 1
	}

	includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
	r := f.NewBuilder().
		Unstructured().
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, &o.FilenameOptions).
		LabelSelectorParam(o.LabelSelector).
		FieldSelectorParam(o.FieldSelector).
		IncludeUninitialized(includeUninitialized).
		SelectAllParam(o.DeleteAll).
		ResourceTypeOrNameArgs(false, args...).RequireObject(false).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}
	o.Result = r

	o.Mapper, err = f.ToRESTMapper()
	if err != nil {
		return err
	}

	o.DynamicClient, err = f.DynamicClient()
	if err != nil {
		return err
	}

	return nil
}

func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
	if o.Output != "" && o.Output != "name" {
		return cmdutil.UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", o.Output)
	}

	if o.DeleteAll && len(o.LabelSelector) > 0 {
		return fmt.Errorf("cannot set --all and --selector at the same time")
	}
	if o.DeleteAll && len(o.FieldSelector) > 0 {
		return fmt.Errorf("cannot set --all and --field-selector at the same time")
	}

	if o.GracePeriod == 0 && !o.ForceDeletion && !o.WaitForDeletion {
		// With the explicit --wait flag we need extra validation for backward compatibility
		return fmt.Errorf("--grace-period=0 must have either --force specified, or --wait to be set to true")
	}

	switch {
	case o.GracePeriod == 0 && o.ForceDeletion:
		fmt.Fprintf(o.ErrOut, "warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.\n")
	case o.ForceDeletion:
		fmt.Fprintf(o.ErrOut, "warning: --force is ignored because --grace-period is not 0.\n")
	}
	return nil
}

func (o *DeleteOptions) RunDelete() error {
	return o.DeleteResult(o.Result)
}

func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
	found := 0
	if o.IgnoreNotFound {
		r = r.IgnoreErrors(errors.IsNotFound)
	}
	err := r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}
		found++

		options := &metav1.DeleteOptions{}
		if o.GracePeriod >= 0 {
			options = metav1.NewDeleteOptions(int64(o.GracePeriod))
		}
		policy := metav1.DeletePropagationForeground
		if !o.Cascade {
			policy = metav1.DeletePropagationOrphan
		}
		options.PropagationPolicy = &policy
		return o.deleteResource(info, options)
	})
	if err != nil {
		return err
	}
	if found == 0 {
		fmt.Fprintf(o.Out, "No resources found\n")
		return nil
	}
	if !o.WaitForDeletion {
		return nil
	}
	// if we don't have a dynamic client, we don't want to wait.  Eventually when delete is cleaned up, this will likely
	// drop out.
	if o.DynamicClient == nil {
		return nil
	}

	effectiveTimeout := o.Timeout
	if effectiveTimeout == 0 {
		// if we requested to wait forever, set it to a week.
		effectiveTimeout = 168 * time.Hour
	}
	waitOptions := kubectlwait.WaitOptions{
		ResourceFinder: genericclioptions.ResourceFinderForResult(r),
		DynamicClient:  o.DynamicClient,
		Timeout:        effectiveTimeout,

		Printer:     printers.NewDiscardingPrinter(),
		ConditionFn: kubectlwait.IsDeleted,
		IOStreams:   o.IOStreams,
	}
	err = waitOptions.RunWait()
	if errors.IsForbidden(err) || errors.IsMethodNotSupported(err) {
		// if we're forbidden from waiting, we shouldn't fail.
		glog.V(1).Info(err)
		return nil
	}
	return err
}

func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) error {
	// TODO: Remove this in or after 1.12 release.
	//       Server version >= 1.11 no longer needs this hack.
	mapping := info.ResourceMapping()
	if mapping.Resource.GroupResource() == (schema.GroupResource{Group: "extensions", Resource: "daemonsets"}) ||
		mapping.Resource.GroupResource() == (schema.GroupResource{Group: "apps", Resource: "daemonsets"}) {
		if err := updateDaemonSet(info.Namespace, info.Name, o.DynamicClient); err != nil {
			return err
		}
	}

	if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil {
		return cmdutil.AddSourceToErr("deleting", info.Source, err)
	}

	o.PrintObj(info)
	return nil
}

// TODO: Remove this in or after 1.12 release.
//       Server version >= 1.11 no longer needs this hack.
func updateDaemonSet(namespace, name string, dynamicClient dynamic.Interface) error {
	dsClient := dynamicClient.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"}).Namespace(namespace)
	obj, err := dsClient.Get(name, metav1.GetOptions{})
	if err != nil {
		return err
	}
	ds := &appsv1.DaemonSet{}
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, ds); err != nil {
		return err
	}

	// We set the nodeSelector to a random label. This label is nearly guaranteed
	// to not be set on any node so the DameonSetController will start deleting
	// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
	// the DaemonSet.
	ds.Spec.Template.Spec.NodeSelector = map[string]string{
		string(uuid.NewUUID()): string(uuid.NewUUID()),
	}
	// force update to avoid version conflict
	ds.ResourceVersion = ""

	out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ds)
	if err != nil {
		return err
	}
	if _, err = dsClient.Update(&unstructured.Unstructured{Object: out}); err != nil {
		return err
	}

	// Wait for the daemon set controller to kill all the daemon pods.
	if err := wait.Poll(1*time.Second, 5*time.Minute, func() (bool, error) {
		updatedObj, err := dsClient.Get(name, metav1.GetOptions{})
		if err != nil {
			return false, nil
		}
		updatedDS := &appsv1.DaemonSet{}
		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(updatedObj.Object, ds); err != nil {
			return false, nil
		}

		return updatedDS.Status.CurrentNumberScheduled+updatedDS.Status.NumberMisscheduled == 0, nil
	}); err != nil {
		return err
	}
	return nil
}

// deletion printing is special because we do not have an object to print.
// This mirrors name printer behavior
func (o *DeleteOptions) PrintObj(info *resource.Info) {
	operation := "deleted"
	groupKind := info.Mapping.GroupVersionKind
	kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
	if len(groupKind.Group) == 0 {
		kindString = strings.ToLower(groupKind.Kind)
	}

	if o.GracePeriod == 0 {
		operation = "force deleted"
	}

	if o.Output == "name" {
		// -o name: prints resource/name
		fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name)
		return
	}

	// understandable output by default
	fmt.Fprintf(o.Out, "%s \"%s\" %s\n", kindString, info.Name, operation)
}
<|endoftext|>


<commit_before>package topom

import (
	"time"

	"github.com/wandoulabs/codis/pkg/models"
	"github.com/wandoulabs/codis/pkg/utils"
	"github.com/wandoulabs/codis/pkg/utils/errors"
	"github.com/wandoulabs/codis/pkg/utils/log"
)

func (s *Topom) ProcessAction(slotId int) error {
	if err := s.PrepareAction(slotId); err != nil {
		return err
	}
	for {
		for s.GetActionDisabled() {
			time.Sleep(time.Millisecond * 10)
		}
		n, err := s.MigrateSlot(slotId)
		if err != nil {
			return err
		}
		switch {
		case n > 0:
			s.NoopInterval()
		case n < 0:
			time.Sleep(time.Millisecond * 10)
		default:
			return s.CompleteAction(slotId)
		}
	}
}

func (s *Topom) NextActionSlotId() int {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if s.closed {
		return -1
	}

	var x *models.SlotMapping
	for _, m := range s.mappings {
		if m.Action.State != models.ActionNothing {
			if x == nil || x.Action.Index > m.Action.Index {
				x = m
			}
		}
	}
	if x == nil {
		return -1
	}
	return x.Id
}

func (s *Topom) NoopInterval() int {
	var ms int
	for !s.IsClosed() {
		if d := s.GetActionInterval() - ms; d <= 0 {
			return ms
		} else {
			d = utils.MinInt(d, 50)
			time.Sleep(time.Millisecond * time.Duration(d))
			ms += d
		}
	}
	return ms
}

func (s *Topom) PrepareAction(slotId int) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.closed {
		return ErrClosedTopom
	}

	m, err := s.getSlotMapping(slotId)
	if err != nil {
		return err
	}
	if m.Action.State == models.ActionNothing {
		return errors.Errorf("action of slot-[%d] is not empty", slotId)
	}

	log.Infof("[%p] prepare action of slot-[%d]\n%s", s, slotId, m.Encode())

	switch m.Action.State {
	case models.ActionPending:

		n := &models.SlotMapping{
			Id:      slotId,
			GroupId: m.GroupId,
			Action:  m.Action,
		}
		n.Action.State = models.ActionPreparing

		if err := s.store.SaveSlotMapping(slotId, n); err != nil {
			log.ErrorErrorf(err, "[%p] update slot-[%d] failed", s, slotId)
			return errors.Errorf("store: update slot-[%d] failed", slotId)
		}

		s.mappings[slotId] = n

		log.Infof("[%p] update slot-[%d]:\n%s", s, slotId, n.Encode())

		fallthrough

	case models.ActionPreparing:

		if err := s.resyncSlotMapping(slotId); err != nil {
			return err
		}

		n := &models.SlotMapping{
			Id:      slotId,
			GroupId: m.GroupId,
			Action:  m.Action,
		}
		n.Action.State = models.ActionMigrating

		if err := s.store.SaveSlotMapping(slotId, n); err != nil {
			log.ErrorErrorf(err, "[%p] update slot-[%d] failed", s, slotId)
			return errors.Errorf("store: update slot-[%d] failed", slotId)
		}

		s.mappings[slotId] = n

		log.Infof("[%p] update slot-[%d]:\n%s", s, slotId, n.Encode())

		fallthrough

	case models.ActionMigrating:

		if err := s.resyncSlotMapping(slotId); err != nil {
			return err
		}

	}
	return nil
}

func (s *Topom) CompleteAction(slotId int) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.closed {
		return ErrClosedTopom
	}

	m, err := s.getSlotMapping(slotId)
	if err != nil {
		return err
	}
	if m.Action.State != models.ActionMigrating {
		return errors.Errorf("action of slot-[%d] is not migrating", slotId)
	}

	log.Infof("[%p] complete action of slot-[%d]\n%s", s, slotId, m.Encode())

	n := &models.SlotMapping{
		Id:      slotId,
		GroupId: m.Action.TargetId,
	}
	s.mappings[slotId] = n

	var rollback = true
	defer func() {
		if rollback {
			s.mappings[slotId] = m
		}
	}()

	if err := s.resyncSlotMapping(slotId); err != nil {
		return err
	}

	if err := s.store.SaveSlotMapping(slotId, n); err != nil {
		log.ErrorErrorf(err, "[%p] update slot-[%d] failed", s, slotId)
		return errors.Errorf("store: update slot-[%d] failed", slotId)
	}

	rollback = false

	log.Infof("[%p] update slot-[%d]:\n%s", s, slotId, n.Encode())

	return nil
}

type actionTask struct {
	From, Dest struct {
		Master  string
		GroupId int
	}
	Locked bool
}

func (s *Topom) newActionTask(slotId int) (*actionTask, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if s.closed {
		return nil, ErrClosedTopom
	}

	m, err := s.getSlotMapping(slotId)
	if err != nil {
		return nil, err
	}
	if m.Action.State != models.ActionMigrating {
		return nil, errors.Errorf("action of slot-[%d] is not migrating", slotId)
	}

	t := &actionTask{
		Locked: s.isSlotLocked(m),
	}
	t.From.Master = s.getGroupMaster(m.GroupId)
	t.From.GroupId = m.GroupId
	t.Dest.Master = s.getGroupMaster(m.Action.TargetId)
	t.Dest.GroupId = m.Action.TargetId

	s.lockGroupMaster(t.From.GroupId)
	s.lockGroupMaster(t.Dest.GroupId)
	return t, nil
}

func (s *Topom) releaseActionTask(t *actionTask) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	s.unlockGroupMaster(t.From.GroupId)
	s.unlockGroupMaster(t.Dest.GroupId)
}

func (s *Topom) MigrateSlot(slotId int) (int, error) {
	t, err := s.newActionTask(slotId)
	if err != nil {
		return 0, err
	}
	defer s.releaseActionTask(t)

	if t.Locked {
		return -1, nil
	}
	if t.From.Master == "" {
		return 0, nil
	}

	c, err := s.redisp.GetClient(t.From.Master)
	if err != nil {
		return 0, err
	}
	defer s.redisp.PutClient(c)
	return c.MigrateSlot(slotId, t.Dest.Master)
}
<commit_msg>Update, log<commit_after>package topom

import (
	"time"

	"github.com/wandoulabs/codis/pkg/models"
	"github.com/wandoulabs/codis/pkg/utils"
	"github.com/wandoulabs/codis/pkg/utils/errors"
	"github.com/wandoulabs/codis/pkg/utils/log"
)

func (s *Topom) ProcessAction(slotId int) error {
	if err := s.PrepareAction(slotId); err != nil {
		return err
	}
	for {
		for s.GetActionDisabled() {
			time.Sleep(time.Millisecond * 10)
		}
		n, err := s.MigrateSlot(slotId)
		if err != nil {
			return err
		}
		switch {
		case n > 0:
			s.NoopInterval()
		case n < 0:
			time.Sleep(time.Millisecond * 10)
		default:
			return s.CompleteAction(slotId)
		}
	}
}

func (s *Topom) NextActionSlotId() int {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if s.closed {
		return -1
	}

	var x *models.SlotMapping
	for _, m := range s.mappings {
		if m.Action.State != models.ActionNothing {
			if x == nil || x.Action.Index > m.Action.Index {
				x = m
			}
		}
	}
	if x == nil {
		return -1
	}
	return x.Id
}

func (s *Topom) NoopInterval() int {
	var ms int
	for !s.IsClosed() {
		if d := s.GetActionInterval() - ms; d <= 0 {
			return ms
		} else {
			d = utils.MinInt(d, 50)
			time.Sleep(time.Millisecond * time.Duration(d))
			ms += d
		}
	}
	return ms
}

func (s *Topom) PrepareAction(slotId int) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.closed {
		return ErrClosedTopom
	}

	m, err := s.getSlotMapping(slotId)
	if err != nil {
		return err
	}
	if m.Action.State == models.ActionNothing {
		return errors.Errorf("action of slot-[%d] is nothing", slotId)
	}

	log.Infof("[%p] prepare action of slot-[%d]\n%s", s, slotId, m.Encode())

	switch m.Action.State {
	case models.ActionPending:

		n := &models.SlotMapping{
			Id:      slotId,
			GroupId: m.GroupId,
			Action:  m.Action,
		}
		n.Action.State = models.ActionPreparing

		if err := s.store.SaveSlotMapping(slotId, n); err != nil {
			log.ErrorErrorf(err, "[%p] update slot-[%d] failed", s, slotId)
			return errors.Errorf("store: update slot-[%d] failed", slotId)
		}

		s.mappings[slotId] = n

		log.Infof("[%p] update slot-[%d]:\n%s", s, slotId, n.Encode())

		fallthrough

	case models.ActionPreparing:

		if err := s.resyncSlotMapping(slotId); err != nil {
			return err
		}

		n := &models.SlotMapping{
			Id:      slotId,
			GroupId: m.GroupId,
			Action:  m.Action,
		}
		n.Action.State = models.ActionMigrating

		if err := s.store.SaveSlotMapping(slotId, n); err != nil {
			log.ErrorErrorf(err, "[%p] update slot-[%d] failed", s, slotId)
			return errors.Errorf("store: update slot-[%d] failed", slotId)
		}

		s.mappings[slotId] = n

		log.Infof("[%p] update slot-[%d]:\n%s", s, slotId, n.Encode())

		fallthrough

	case models.ActionMigrating:

		if err := s.resyncSlotMapping(slotId); err != nil {
			return err
		}

	}
	return nil
}

func (s *Topom) CompleteAction(slotId int) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.closed {
		return ErrClosedTopom
	}

	m, err := s.getSlotMapping(slotId)
	if err != nil {
		return err
	}
	if m.Action.State != models.ActionMigrating {
		return errors.Errorf("action of slot-[%d] is not migrating", slotId)
	}

	log.Infof("[%p] complete action of slot-[%d]\n%s", s, slotId, m.Encode())

	n := &models.SlotMapping{
		Id:      slotId,
		GroupId: m.Action.TargetId,
	}
	s.mappings[slotId] = n

	var rollback = true
	defer func() {
		if rollback {
			s.mappings[slotId] = m
		}
	}()

	if err := s.resyncSlotMapping(slotId); err != nil {
		return err
	}

	if err := s.store.SaveSlotMapping(slotId, n); err != nil {
		log.ErrorErrorf(err, "[%p] update slot-[%d] failed", s, slotId)
		return errors.Errorf("store: update slot-[%d] failed", slotId)
	}

	rollback = false

	log.Infof("[%p] update slot-[%d]:\n%s", s, slotId, n.Encode())

	return nil
}

type actionTask struct {
	From, Dest struct {
		Master  string
		GroupId int
	}
	Locked bool
}

func (s *Topom) newActionTask(slotId int) (*actionTask, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if s.closed {
		return nil, ErrClosedTopom
	}

	m, err := s.getSlotMapping(slotId)
	if err != nil {
		return nil, err
	}
	if m.Action.State != models.ActionMigrating {
		return nil, errors.Errorf("action of slot-[%d] is not migrating", slotId)
	}

	t := &actionTask{
		Locked: s.isSlotLocked(m),
	}
	t.From.Master = s.getGroupMaster(m.GroupId)
	t.From.GroupId = m.GroupId
	t.Dest.Master = s.getGroupMaster(m.Action.TargetId)
	t.Dest.GroupId = m.Action.TargetId

	s.lockGroupMaster(t.From.GroupId)
	s.lockGroupMaster(t.Dest.GroupId)
	return t, nil
}

func (s *Topom) releaseActionTask(t *actionTask) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	s.unlockGroupMaster(t.From.GroupId)
	s.unlockGroupMaster(t.Dest.GroupId)
}

func (s *Topom) MigrateSlot(slotId int) (int, error) {
	t, err := s.newActionTask(slotId)
	if err != nil {
		return 0, err
	}
	defer s.releaseActionTask(t)

	if t.Locked {
		return -1, nil
	}
	if t.From.Master == "" {
		return 0, nil
	}

	c, err := s.redisp.GetClient(t.From.Master)
	if err != nil {
		return 0, err
	}
	defer s.redisp.PutClient(c)
	return c.MigrateSlot(slotId, t.Dest.Master)
}
<|endoftext|>


<commit_before>package procfs

type ProcFS struct {
	Processes map[int]Process
	Self      Process
}

type Process struct {
	Auxv    []byte
	Cmdline []string
	Cwd     string
	Environ map[string]string
	Exe     string
	Fds     map[int]Fd
	Root    string
	Status  map[string]string
	Threads map[int]Thread
}

// TODO limits, maps, mem, mountinfo, mounts, mountstats, ns, smaps, stat

type Fd struct {
	Path  string
	Pos   int
	Flags int
}

type Thread struct {
	// TODO
}
<commit_msg>Continuing to fill out constants<commit_after>package procfs

type Filler interface {
	Fill()
}

type Lister interface {
	List(string)
}

type Getter interface {
	Get(string)
}

type ProcFS struct {
	Processes map[int]*Process
	Self      int
}

const (
	PROCFS_PROCESSES = "Processes"
	PROCFS_SELF = "Self"
)

func (pfs *ProcFS) Fill() {
	pfs.List(PROCFS_PROCESSES)
	pfs.Get(PROCFS_SELF)
}

func (pfs *ProcFS) List(k string) {
	switch k {
	case PROCFS_PROCESSES:
	}
}

func (pfs *ProcFS) Get(k string) {
	switch k {
	case PROCFS_SELF:
	}
}

type Process struct {
	Auxv    []byte
	Cmdline []string
	Cwd     string
	Environ map[string]string
	Exe     string
	Fds     map[int]*Fd
	Root    string
	Status  map[string]string
	Threads map[int]*Thread
}

const (
	PROCFS_PROC_AUXV = "Process.Auxv"
	PROCFS_PROC_CMDLINE = "Process.Cmdline"
	PROCFS_PROC_CWD = "Process.Cwd"
	PROCFS_PROC_ENVIRON = "Process.Environ"
	PROCFS_PROC_EXE = "Process.Exe"
	PROCFS_PROC_ROOT = "Process.Root"
	PROCFS_PROC_STATUS = "Process.Status"

	PROCFS_PROC_FDS = "Process.Fds"
	PROCFS_PROC_THREADS = "Process.Threads"
)

func (p *Process) Fill() {
	p.Get(PROCFS_PROC_AUXV)
	p.Get(PROCFS_PROC_CMDLINE)
	p.Get(PROCFS_PROC_CWD)
	p.Get(PROCFS_PROC_ENVIRON)
	p.Get(PROCFS_PROC_EXE)
	p.Get(PROCFS_PROC_ROOT)
	p.Get(PROCFS_PROC_STATUS)

	// Fds
	p.List(PROCFS_PROC_FDS)
	for _, f := range p.Fds {
		f.Fill()
	}

	// Threads
	p.List(PROCFS_PROC_THREADS)
	for _, t := range p.Threads {
		t.Fill()
	}
}

func (p *Process) List(k string) {

}

func (p *Process) Get(k string) {

}
// TODO limits, maps, mem, mountinfo, mounts, mountstats, ns, smaps, stat

type Fd struct {
	Path  string
	Pos   int
	Flags int
}

const (
	PROCFS_PROC_FD_PATH = "Process.Fd.Path"
	PROCFS_PROC_FD_POS = "Process.Fd.Pos"
	PROCFS_PROC_FD_FLAGS = "Process.Fd.Flags"
)

func (f *Fd) Fill() {
	f.Get(PROCFS_PROC_FD_PATH)
	f.Get(PROCFS_PROC_FD_POS)
	f.Get(PROCFS_PROC_FD_FLAGS)
}

func (f *Fd) Get(k string) {
	switch k {

	}
}

type Thread struct {
	// TODO
}

func (t *Thread) Fill() {

}

func (t *Thread) Get(k string) {

}
<|endoftext|>


<commit_before>package main

import "testing"

// func TestCheckEnvUrl_junk(t *testing.T) {
// 	result := CheckEnvUrl([]string{`PATH`, `bin:bin/bash`})
// 	if result != "" {
// 		t.Error(`Expected "", got`, result)
// 	}
// }

// func TestCheckEnvUrl_good_key_bad_url(t *testing.T) {

// 	result := CheckEnvUrl([]string{`CHAT_URL`, `bin:bin/bash`})
// 	if result != "" {
// 		t.Error(`Expected "", got`, result)
// 	}
// }

// func TestCheckEnvUrl_bad_key_good_url(t *testing.T) {
// 	result := CheckEnvUrl([]string{`FOO`, `http://example.com/blah`})
// 	if result != "" {
// 		t.Error(`Expected "", got`, result)
// 	}
// }

// func TestCheckEnvUrl_correct(t *testing.T) {

// 	result := CheckEnvUrl([]string{`FOO_URL`, `http://example.com/blah`})
// 	if result != `http://example.com/blah` {
// 		t.Error(`Expected "http://example.com/blah", got`, result)
// 	}

// }
<commit_msg>Delete config_test.go<commit_after><|endoftext|>


<commit_before>package main

import (
	"bytes"
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
)

var configTestInstance *machine

func TestInstanceName(t *testing.T) {
	n := NewNova()
	n.Init()

	machines, _ := n.List()
	if len(machines) == 0 {
		t.Skipf("No machines found. Skip this test.")
	}

	configTestInstance = machines[0]
}

// ---------------------------------------

// Only instance name in the arguments.
func TestParseArgs1(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		configTestInstance.Name,
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_SSH {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

// Instance name with user in the arguments.
func TestParseArgs2(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		"root@" + configTestInstance.Name,
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_SSH {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "root" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

// Instance name with user and remote commands in the arguments
func TestParseArgs3(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		"root@" + configTestInstance.Name,
		"test-command",
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_SSH {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "root" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "test-command" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

// With SSH options
func TestParseArgs4(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		// Port fowarding option for ssh
		"-L",
		"54321:localhost:54321",
		configTestInstance.Name,
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_SSH {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshOptions[0] != "-L" || c.SshOptions[1] != "54321:localhost:54321" {
		t.Errorf("ssh options are not match: %v", c)
	}
	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

func TestHelp(t *testing.T) {
	args := []string{
		"--novassh-help",
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_HELP {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}
}

func TestList(t *testing.T) {
	args := []string{
		"--novassh-list",
	}

	c := &Config{
		Stdout: new(bytes.Buffer),
		Stdin:  nil,
		Stderr: nil,
		Args:   args,
	}
	cmd, err := c.ParseArgs()
	if cmd != CMD_LIST {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}
}

func TestDeauth(t *testing.T) {
	args := []string{
		"--novassh-deauth",
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_DEAUTH {
		t.Errorf("Command should be CMD_SSH: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	nova := NewNova()
	_, err = os.Stat(nova.credentialCachePath())
	if err != nil {
		t.Errorf("Credential cache file sill exists")
	}
}

func TestDebug(t *testing.T) {
	args := []string{
		"--novassh-debug",
	}

	c := &Config{
		Stdout: new(bytes.Buffer),
		Stdin:  nil,
		Stderr: nil,
		Args:   args,
	}
	_, err := c.ParseArgs()
	if err != nil {
		t.Errorf("%v", err)
	}

	// disable debug
	disableDebugTransport()
	logrus.SetLevel(logrus.InfoLevel)
}

func TestConsole(t *testing.T) {
	args := []string{
		"--novassh-console",
		configTestInstance.Name,
	}

	c := &Config{Args: args}
	_, err := c.ParseArgs()
	if c.ConnType != CON_CONSOLE {
		t.Errorf("ConnType should be CON_CONSOLE: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}
}
<commit_msg>コンソールのテストがこけていたので修正<commit_after>package main

import (
	"bytes"
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
)

var configTestInstance *machine

func TestInstanceName(t *testing.T) {
	n := NewNova()
	n.Init()

	machines, _ := n.List()
	if len(machines) == 0 {
		t.Skipf("No machines found. Skip this test.")
	}

	configTestInstance = machines[0]
}

// ---------------------------------------

// Only instance name in the arguments.
func TestParseArgs1(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		configTestInstance.Name,
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_CONNECT {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

// Instance name with user in the arguments.
func TestParseArgs2(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		"root@" + configTestInstance.Name,
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_CONNECT {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "root" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

// Instance name with user and remote commands in the arguments
func TestParseArgs3(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		"root@" + configTestInstance.Name,
		"test-command",
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_CONNECT {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "root" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "test-command" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

// With SSH options
func TestParseArgs4(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		// Port fowarding option for ssh
		"-L",
		"54321:localhost:54321",
		configTestInstance.Name,
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_CONNECT {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.ConnType != CON_SSH {
		t.Errorf("ConnType should be CON_SSH: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	if c.SshOptions[0] != "-L" || c.SshOptions[1] != "54321:localhost:54321" {
		t.Errorf("ssh options are not match: %v", c)
	}
	if c.SshHost != configTestInstance.Ipaddr {
		t.Errorf("hostname is not match: %v", c)
	}
	if c.SshUser != "" {
		t.Errorf("username is not match: %v", c)
	}
	if c.SshRemoteCommand != "" {
		t.Errorf("remote-command is not match: %v", c)
	}
}

func TestHelp(t *testing.T) {
	args := []string{
		"--novassh-help",
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_HELP {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}
}

func TestList(t *testing.T) {
	args := []string{
		"--novassh-list",
	}

	c := &Config{
		Stdout: new(bytes.Buffer),
		Stdin:  nil,
		Stderr: nil,
		Args:   args,
	}
	cmd, err := c.ParseArgs()
	if cmd != CMD_LIST {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}
}

func TestDeauth(t *testing.T) {
	args := []string{
		"--novassh-deauth",
	}

	c := &Config{Args: args}
	cmd, err := c.ParseArgs()
	if cmd != CMD_DEAUTH {
		t.Errorf("Command should be CMD_CONNECT: command=%d", cmd)
	} else if err != nil {
		t.Errorf("%v", err)
	}

	nova := NewNova()
	_, err = os.Stat(nova.credentialCachePath())
	if err != nil {
		t.Errorf("Credential cache file sill exists")
	}
}

func TestDebug(t *testing.T) {
	args := []string{
		"--novassh-debug",
	}

	c := &Config{
		Stdout: new(bytes.Buffer),
		Stdin:  nil,
		Stderr: nil,
		Args:   args,
	}
	_, err := c.ParseArgs()
	if err != nil {
		t.Errorf("%v", err)
	}

	// disable debug
	disableDebugTransport()
	logrus.SetLevel(logrus.InfoLevel)
}

func TestConsole(t *testing.T) {
	if configTestInstance == nil {
		t.Skipf("No servers found. Skip this test.")
	}

	args := []string{
		"--novassh-console",
		configTestInstance.Name,
	}

	c := &Config{Args: args}
	_, err := c.ParseArgs()
	if c.ConnType != CON_CONSOLE {
		t.Errorf("ConnType should be CON_CONSOLE: type=%d", c.ConnType)
	} else if err != nil {
		t.Errorf("%v", err)
	}
}
<|endoftext|>


<commit_before>package qbit

import (
	"fmt"
	"strings"
)

// builtin constraints that should be used with tag
// e.g;
// type User struct {
//		Id int
//		PrimaryKey `qbit:"id"`
// }
type PrimaryKey Constraint
type ForeignKey Constraint
type CompositeUnique Constraint

type Constraint struct {
	Name string
}

// function generates generic null constraint
func Null() Constraint {
	return Constraint{"NULL"}
}

// function generates generic not null constraint
func NotNull() Constraint {
	return Constraint{"NOT NULL"}
}

// function generates generic default constraint
func Default(value interface{}) Constraint {
	return Constraint{fmt.Sprintf("DEFAULT `%v`", value)}
}

// function generates generic unique constraint
// if cols are givern, then composite unique constraint will be built
func Unique(cols ...string) Constraint {
	if len(cols) == 0 {
		return Constraint{"UNIQUE"}
	}
	return Constraint{fmt.Sprintf("UNIQUE(%s)", strings.Join(cols, ", "))}
}

// function generates generic primary key syntax
// if cols are given, then composite primary key will be built
func Primary(cols ...string) Constraint {
	if len(cols) == 0 {
		return Constraint{"PRIMARY KEY"}
	}
	constraint := Constraint{fmt.Sprintf("PRIMARY KEY(%s)", strings.Join(cols, ", "))}
	return constraint
}

// function generates generic foreign key syntax
func Foreign(cols string, reftable string, refcols string) Constraint {
	constraint := Constraint{
		fmt.Sprintf(
			"FOREIGN KEY (%s) REFERENCES %s(%s)",
			cols,
			reftable,
			refcols,
		),
	}
	return constraint
}
<commit_msg>fix constraint lint errors<commit_after>package qbit

import (
	"fmt"
	"strings"
)

// PrimaryKey is the builtin constraints that should be used with tag
// type User struct {
//		Id int
//		PrimaryKey `qbit:"id"`
// }
type PrimaryKey Constraint

// ForeignKey is the builtin constraint that should be used with tag
type ForeignKey Constraint

// CompositeUnique is the builtin multiple unique constraint that should be used with tag
type CompositeUnique Constraint

// Constraint is the generic struct for table level and column level constraints
type Constraint struct {
	Name string
}

// Null generates generic null constraint
func Null() Constraint {
	return Constraint{"NULL"}
}

// NotNull generates generic not null constraint
func NotNull() Constraint {
	return Constraint{"NOT NULL"}
}

// Default generates generic default constraint
func Default(value interface{}) Constraint {
	return Constraint{fmt.Sprintf("DEFAULT `%v`", value)}
}

// Unique generates generic unique constraint
// if cols are given, then composite unique constraint will be built
func Unique(cols ...string) Constraint {
	if len(cols) == 0 {
		return Constraint{"UNIQUE"}
	}
	return Constraint{fmt.Sprintf("UNIQUE(%s)", strings.Join(cols, ", "))}
}

// Primary generates generic primary key syntax
// if cols are given, then composite primary key will be built
func Primary(cols ...string) Constraint {
	if len(cols) == 0 {
		return Constraint{"PRIMARY KEY"}
	}
	constraint := Constraint{fmt.Sprintf("PRIMARY KEY(%s)", strings.Join(cols, ", "))}
	return constraint
}

// Foreign generates generic foreign key syntax
func Foreign(cols string, reftable string, refcols string) Constraint {
	constraint := Constraint{
		fmt.Sprintf(
			"FOREIGN KEY (%s) REFERENCES %s(%s)",
			cols,
			reftable,
			refcols,
		),
	}
	return constraint
}
<|endoftext|>


<commit_before>/* https://leetcode.com/problems/trapping-rain-water/description/
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

http://www.leetcode.com/static/images/problemset/rainwatertrap.png
*/

package leetcode

func trap(height []int) int {
	return -1
}
<commit_msg>add trap<commit_after>/* https://leetcode.com/problems/trapping-rain-water/description/
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

http://www.leetcode.com/static/images/problemset/rainwatertrap.png
*/

package leetcode

func trap(height []int) int {
	// 2 pointers
	var (
		left, right       = 0, len(height) - 1
		res               = 0
		leftMax, rightMax = 0, 0
	)

	for left < right {
		if height[left] < height[right] {
			if height[left] >= leftMax {
				leftMax = height[left]
			} else {
				res += leftMax - height[left]
			}
			left++
		} else {
			if height[right] >= rightMax {
				rightMax = height[right]
			} else {
				res += rightMax - height[right]
			}
			right--
		}
	}
	return res
}
<|endoftext|>


<commit_before>// This file is subject to a 1-clause BSD license.
// Its Datas can be found in the enclosed LICENSE file.

package proto

import (
	"io"
	"strconv"
	"strings"
)

// Message is a parsed incoming message.
type Message struct {
	SenderName string
	SenderMask string
	Receiver   string
	Data       string
	Command    uint16
}

// FromChannel returns true if this message came from a channel context
// instead of a user or service.
func (m *Message) FromChannel() bool {
	if len(m.Receiver) == 0 {
		return false
	}

	c := m.Receiver[0]
	return c == '#' || c == '&' || c == '!' || c == '+'
}

// parseMessage parses a message from the given data.
func parseMessage(data string) (m *Message, err error) {
	if len(data) == 0 {
		return nil, io.EOF
	}

	m = new(Message)
	m.Command = Unknown
	m.Data = data

	list := strings.Split(data, " ")

	switch list[0] {
	case "PING":
		m.Command = CmdPing
		m.Data = list[1][1:]
		return

	case "ERROR":
		m.Command = CmdError
		m.Data = list[1][1:]
		return
	}

	if len(list) < 3 {
		return
	}

	m.SenderMask = list[0][1:]
	m.Command = findType(list[1])
	m.Receiver = list[2]
	m.Data = strings.Join(list[3:], " ")

	idx := strings.Index(m.SenderMask, "!")
	if idx > -1 {
		m.SenderName = m.SenderMask[:idx]
		m.SenderMask = m.SenderMask[idx+1:]
	}

	if len(m.Data) > 0 && m.Data[0] == ':' {
		m.Data = m.Data[1:]
	}

	if !m.FromChannel() {
		m.Receiver = m.SenderName
	}

	return
}

// findType attempts to parse a command or reply type from the input string.
// These come as 3-digit numbers or a string. For example: "001" or "NOTICE"
func findType(v string) uint16 {
	n, err := strconv.ParseUint(v, 10, 16)
	if err == nil {
		return uint16(n)
	}

	v = strings.ToUpper(v)

	switch v {
	case "ADMIN":
		return CmdAdmin
	case "AWAY":
		return CmdAway
	case "CONNECT":
		return CmdConnect
	case "DIE":
		return CmdDie
	case "ERROR":
		return CmdError
	case "INFO":
		return CmdInfo
	case "INVITE":
		return CmdInvite
	case "ISON":
		return CmdIsOn
	case "JOIN":
		return CmdJoin
	case "KICK":
		return CmdKick
	case "KILL":
		return CmdKill
	case "LINKS":
		return CmdLinks
	case "LIST":
		return CmdList
	case "LUSERS":
		return CmdLUsers
	case "MODE":
		return CmdMode
	case "MOTD":
		return CmdMOTD
	case "NAMES":
		return CmdNames
	case "NICK":
		return CmdNick
	case "NJOIN":
		return CmdNJoin
	case "NOTICE":
		return CmdNotice
	case "OPER":
		return CmdOper
	case "PART":
		return CmdPart
	case "PASS":
		return CmdPass
	case "PING":
		return CmdPing
	case "PONG":
		return CmdPong
	case "PRIVMSG":
		return CmdPrivMsg
	case "QUIT":
		return CmdQuit
	case "REHASH":
		return CmdRehash
	case "RESTART":
		return CmdRestart
	case "SERVER":
		return CmdServer
	case "SERVICE":
		return CmdService
	case "SERVLIST":
		return CmdServList
	case "SQUERY":
		return CmdSQuery
	case "SQUIRT":
		return CmdSquirt
	case "SQUIT":
		return CmdSQuit
	case "STATS":
		return CmdStats
	case "SUMMON":
		return CmdSummon
	case "TIME":
		return CmdTime
	case "TOPIC":
		return CmdTopic
	case "TRACE":
		return CmdTrace
	case "USER":
		return CmdUser
	case "USERHOST":
		return CmdUserHost
	case "USERS":
		return CmdUsers
	case "VERSION":
		return CmdVersion
	case "WALLOPS":
		return CmdWAllOps
	case "WHO":
		return CmdWho
	case "WHOIS":
		return CmdWhoIs
	case "WHOWAS":
		return CmdWhoWas
	}

	return Unknown
}
<commit_msg>proto: Adds code comments.<commit_after>// This file is subject to a 1-clause BSD license.
// Its Datas can be found in the enclosed LICENSE file.

package proto

import (
	"io"
	"strconv"
	"strings"
)

// Message is a parsed incoming message.
type Message struct {
	SenderName string
	SenderMask string
	Receiver   string
	Data       string
	Command    uint16
}

// FromChannel returns true if this message came from a channel context
// instead of a user or service.
func (m *Message) FromChannel() bool {
	if len(m.Receiver) == 0 {
		return false
	}

	c := m.Receiver[0]
	return c == '#' || c == '&' || c == '!' || c == '+'
}

// parseMessage parses a message from the given data.
func parseMessage(data string) (m *Message, err error) {
	if len(data) == 0 {
		return nil, io.EOF
	}

	m = new(Message)
	m.Command = Unknown
	m.Data = data

	list := strings.Split(data, " ")

	switch list[0] {
	case "PING":
		m.Command = CmdPing
		m.Data = list[1][1:]
		return

	case "ERROR":
		m.Command = CmdError
		m.Data = list[1][1:]
		return
	}

	if len(list) < 3 {
		return
	}

	m.SenderMask = list[0][1:]
	m.Command = findType(list[1])
	m.Receiver = list[2]
	m.Data = strings.Join(list[3:], " ")

	idx := strings.Index(m.SenderMask, "!")
	if idx > -1 {
		m.SenderName = m.SenderMask[:idx]
		m.SenderMask = m.SenderMask[idx+1:]
	}

	if len(m.Data) > 0 && m.Data[0] == ':' {
		m.Data = m.Data[1:]
	}

	if !m.FromChannel() {
		// Some messages supply our own nickname as the receiver.
		// If we want to send messages back to the origin,
		// we should reset the receiver to the sender's name.
		m.Receiver = m.SenderName
	}

	return
}

// findType attempts to parse a command or reply type from the input string.
// These come as 3-digit numbers or a string. For example: "001" or "NOTICE"
func findType(v string) uint16 {
	n, err := strconv.ParseUint(v, 10, 16)
	if err == nil {
		return uint16(n)
	}

	v = strings.ToUpper(v)

	switch v {
	case "ADMIN":
		return CmdAdmin
	case "AWAY":
		return CmdAway
	case "CONNECT":
		return CmdConnect
	case "DIE":
		return CmdDie
	case "ERROR":
		return CmdError
	case "INFO":
		return CmdInfo
	case "INVITE":
		return CmdInvite
	case "ISON":
		return CmdIsOn
	case "JOIN":
		return CmdJoin
	case "KICK":
		return CmdKick
	case "KILL":
		return CmdKill
	case "LINKS":
		return CmdLinks
	case "LIST":
		return CmdList
	case "LUSERS":
		return CmdLUsers
	case "MODE":
		return CmdMode
	case "MOTD":
		return CmdMOTD
	case "NAMES":
		return CmdNames
	case "NICK":
		return CmdNick
	case "NJOIN":
		return CmdNJoin
	case "NOTICE":
		return CmdNotice
	case "OPER":
		return CmdOper
	case "PART":
		return CmdPart
	case "PASS":
		return CmdPass
	case "PING":
		return CmdPing
	case "PONG":
		return CmdPong
	case "PRIVMSG":
		return CmdPrivMsg
	case "QUIT":
		return CmdQuit
	case "REHASH":
		return CmdRehash
	case "RESTART":
		return CmdRestart
	case "SERVER":
		return CmdServer
	case "SERVICE":
		return CmdService
	case "SERVLIST":
		return CmdServList
	case "SQUERY":
		return CmdSQuery
	case "SQUIRT":
		return CmdSquirt
	case "SQUIT":
		return CmdSQuit
	case "STATS":
		return CmdStats
	case "SUMMON":
		return CmdSummon
	case "TIME":
		return CmdTime
	case "TOPIC":
		return CmdTopic
	case "TRACE":
		return CmdTrace
	case "USER":
		return CmdUser
	case "USERHOST":
		return CmdUserHost
	case "USERS":
		return CmdUsers
	case "VERSION":
		return CmdVersion
	case "WALLOPS":
		return CmdWAllOps
	case "WHO":
		return CmdWho
	case "WHOIS":
		return CmdWhoIs
	case "WHOWAS":
		return CmdWhoWas
	}

	return Unknown
}
<|endoftext|>