<commit_before>package lib

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/spf13/viper"
	ehttp "github.com/topfreegames/extensions/http"
)

// Khan is a struct that represents a khan API application
type Khan struct {
	httpClient *http.Client
	Config     *viper.Viper
	url        string
	user       string
	pass       string
	gameID     string
}

var (
	client *http.Client
	once   sync.Once
)

func getHTTPClient(timeout time.Duration) *http.Client {
	once.Do(func() {
		client = &http.Client{
			Timeout: timeout,
		}
		ehttp.Instrument(client)
	})
	return client
}

// NewKhan returns a new khan API application
func NewKhan(config *viper.Viper) KhanInterface {
	config.SetDefault("khan.timeout", 500*time.Millisecond)
	k := &Khan{
		httpClient: getHTTPClient(config.GetDuration("khan.timeout")),
		Config:     config,
		url:        config.GetString("khan.url"),
		user:       config.GetString("khan.user"),
		pass:       config.GetString("khan.pass"),
		gameID:     config.GetString("khan.gameid"),
	}
	return k
}

func (k *Khan) sendTo(ctx context.Context, method, url string, payload interface{}) ([]byte, error) {
	payloadJSON, err := json.Marshal(payload)
	if err != nil {
		return nil, err
	}

	var req *http.Request

	if payload != nil {
		req, err = http.NewRequest(method, url, bytes.NewBuffer(payloadJSON))
		if err != nil {
			return nil, err
		}
	} else {
		req, err = http.NewRequest(method, url, nil)
		if err != nil {
			return nil, err
		}
	}
	req.Header.Set("Content-Type", "application/json")
	req.SetBasicAuth(k.user, k.pass)
	if ctx == nil {
		ctx = context.Background()
	}
	req = req.WithContext(ctx)

	resp, err := k.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, respErr := ioutil.ReadAll(resp.Body)
	if respErr != nil {
		return nil, respErr
	}

	if resp.StatusCode > 399 {
		return nil, newRequestError(resp.StatusCode, string(body))
	}

	return body, nil
}

func (k *Khan) buildURL(pathname string) string {
	return fmt.Sprintf("%s/games/%s/%s", k.url, k.gameID, pathname)
}

func (k *Khan) buildCreatePlayerURL() string {
	pathname := "players"
	return k.buildURL(pathname)
}

func (k *Khan) buildUpdatePlayerURL(playerID string) string {
	pathname := fmt.Sprintf("players/%s", playerID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrievePlayerURL(playerID string) string {
	pathname := fmt.Sprintf("players/%s", playerID)
	return k.buildURL(pathname)
}

func (k *Khan) buildCreateClanURL() string {
	pathname := "clans"
	return k.buildURL(pathname)
}

func (k *Khan) buildUpdateClanURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrieveClanURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrieveClanSummaryURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/summary", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrieveClansSummaryURL(clanIDs []string) string {
	pathname := fmt.Sprintf("clans-summary?clanPublicIds=%s", strings.Join(clanIDs, ","))
	return k.buildURL(pathname)
}

func (k *Khan) buildApplyForMembershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/application", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildInviteForMembershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/invitation", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildApproveDenyMembershipApplicationURL(clanID, action string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/application/%s", clanID, action)
	return k.buildURL(pathname)
}

func (k *Khan) buildApproveDenyMembershipInvitationURL(clanID, action string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/invitation/%s", clanID, action)
	return k.buildURL(pathname)
}

func (k *Khan) buildPromoteDemoteURL(clanID, action string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/%s", clanID, action)
	return k.buildURL(pathname)
}

func (k *Khan) buildDeleteMembershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/delete", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildLeaveClanURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/leave", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildTransferOwnershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/transfer-ownership", clanID)
	return k.buildURL(pathname)
}

// CreatePlayer calls Khan to create a new player
func (k *Khan) CreatePlayer(ctx context.Context, publicID, name string, metadata interface{}) (string, error) {
	route := k.buildCreatePlayerURL()
	playerPayload := &Player{
		PublicID: publicID,
		Name:     name,
		Metadata: metadata,
	}
	body, err := k.sendTo(ctx, "POST", route, playerPayload)

	if err != nil {
		return "", err
	}

	var player Player
	err = json.Unmarshal(body, &player)

	return player.PublicID, err
}

// UpdatePlayer calls khan to update the player
func (k *Khan) UpdatePlayer(
	ctx context.Context,
	publicID, name string,
	metadata interface{},
) (*Result, error) {
	route := k.buildUpdatePlayerURL(publicID)
	playerPayload := &Player{Name: name, Metadata: metadata}
	body, err := k.sendTo(ctx, "PUT", route, playerPayload)
	if err != nil {
		return nil, err
	}

	var result Result
	err = json.Unmarshal(body, &result)
	return &result, err
}

// RetrievePlayer calls the retrieve player route from khan
func (k *Khan) RetrievePlayer(ctx context.Context, publicID string) (*Player, error) {
	route := k.buildRetrievePlayerURL(publicID)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var player Player
	err = json.Unmarshal(body, &player)
	return &player, err
}

// CreateClan calls the create clan route from khan
func (k *Khan) CreateClan(ctx context.Context, clan *ClanPayload) (string, error) {
	route := k.buildCreateClanURL()
	body, err := k.sendTo(ctx, "POST", route, clan)

	if err != nil {
		return "", err
	}

	var player Player
	err = json.Unmarshal(body, &player)
	return player.PublicID, err
}

// UpdateClan calls the update clan route from khan
func (k *Khan) UpdateClan(ctx context.Context, clan *ClanPayload) (*Result, error) {
	route := k.buildUpdateClanURL(clan.PublicID)
	body, err := k.sendTo(ctx, "PUT", route, clan)
	if err != nil {
		return nil, err
	}

	var result Result
	err = json.Unmarshal(body, &result)
	return &result, err
}

// RetrieveClanSummary calls the route to retrieve clan summary from khan
func (k *Khan) RetrieveClanSummary(ctx context.Context, clanID string) (*ClanSummary, error) {
	route := k.buildRetrieveClanSummaryURL(clanID)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var clanSummary ClanSummary
	err = json.Unmarshal(body, &clanSummary)
	return &clanSummary, err
}

// RetrieveClansSummary calls the route to retrieve clans summary from khan
func (k *Khan) RetrieveClansSummary(ctx context.Context, clanIDs []string) ([]*ClanSummary, error) {
	route := k.buildRetrieveClansSummaryURL(clanIDs)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var clansSummary ClansSummary
	err = json.Unmarshal(body, &clansSummary)
	if err != nil {
		return nil, err
	}
	return clansSummary.Clans, nil
}

// RetrieveClan calls the route to retrieve clan from khan
func (k *Khan) RetrieveClan(ctx context.Context, clanID string) (*Clan, error) {
	route := k.buildRetrieveClanURL(clanID)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var clan Clan
	err = json.Unmarshal(body, &clan)
	return &clan, err
}

// ApplyForMembership calls apply for membership route on khan
func (k *Khan) ApplyForMembership(
	ctx context.Context,
	payload *ApplicationPayload,
) (*ClanApplyResult, error) {
	route := k.buildApplyForMembershipURL(payload.ClanID)
	body, err := k.sendTo(ctx, "POST", route, payload)

	if err != nil {
		return nil, err
	}

	var application ClanApplyResult
	err = json.Unmarshal(body, &application)
	return &application, err
}

// InviteForMembership invites a clan member to join clan
func (k *Khan) InviteForMembership(
	ctx context.Context,
	payload *InvitationPayload,
) (*Result, error) {
	route := k.buildInviteForMembershipURL(payload.ClanID)
	return k.defaultPostRequest(ctx, route, payload)
}

// ApproveDenyMembershipApplication approves or deny player
// application on clan
func (k *Khan) ApproveDenyMembershipApplication(
	ctx context.Context,
	payload *ApplicationApprovalPayload,
) (*Result, error) {
	route := k.buildApproveDenyMembershipApplicationURL(payload.ClanID, payload.Action)
	return k.defaultPostRequest(ctx, route, payload)
}

// ApproveDenyMembershipInvitation approves or deny player
// invitation on clan
func (k *Khan) ApproveDenyMembershipInvitation(
	ctx context.Context,
	payload *InvitationApprovalPayload,
) (*Result, error) {
	route := k.buildApproveDenyMembershipInvitationURL(payload.ClanID, payload.Action)
	return k.defaultPostRequest(ctx, route, payload)
}

// PromoteDemote promotes or demotes player on clan
func (k *Khan) PromoteDemote(
	ctx context.Context,
	payload *PromoteDemotePayload,
) (*Result, error) {
	route := k.buildPromoteDemoteURL(payload.ClanID, payload.Action)
	return k.defaultPostRequest(ctx, route, payload)
}

// DeleteMembership deletes membership on clan
func (k *Khan) DeleteMembership(
	ctx context.Context,
	payload *DeleteMembershipPayload,
) (*Result, error) {
	route := k.buildDeleteMembershipURL(payload.ClanID)
	return k.defaultPostRequest(ctx, route, payload)
}

// LeaveClan allows member to leave clan
func (k *Khan) LeaveClan(
	ctx context.Context,
	clanID string,
) (*LeaveClanResult, error) {
	route := k.buildLeaveClanURL(clanID)
	body, err := k.sendTo(ctx, "POST", route, nil)

	if err != nil {
		return nil, err
	}

	var result LeaveClanResult
	err = json.Unmarshal(body, &result)
	return &result, err
}

// TransferOwnership transfers clan ownership to another member
func (k *Khan) TransferOwnership(
	ctx context.Context,
	playerPublicID, clanID string,
) (*TransferOwnershipResult, error) {
	route := k.buildTransferOwnershipURL(clanID)
	body, err := k.sendTo(ctx, "POST", route, map[string]interface{}{
		"playerPublicID": playerPublicID,
	})

	if err != nil {
		return nil, err
	}

	var result TransferOwnershipResult
	err = json.Unmarshal(body, &result)
	return &result, err
}

func (k *Khan) defaultPostRequest(
	ctx context.Context,
	route string,
	payload interface{},
) (*Result, error) {
	body, err := k.sendTo(ctx, "POST", route, payload)
	if err != nil {
		return nil, err
	}

	var result Result
	err = json.Unmarshal(body, &result)
	return &result, err
}
<commit_msg>maxIdleConns config on lib<commit_after>package lib

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/spf13/viper"
	ehttp "github.com/topfreegames/extensions/http"
)

// Khan is a struct that represents a khan API application
type Khan struct {
	httpClient *http.Client
	Config     *viper.Viper
	url        string
	user       string
	pass       string
	gameID     string
}

var (
	client *http.Client
	once   sync.Once
)

func getHTTPClient(
	timeout time.Duration,
	maxIdleConns, maxIdleConnsPerHost int,
) *http.Client {
	once.Do(func() {
		client = &http.Client{
			Transport: getHTTPTransport(maxIdleConns, maxIdleConnsPerHost),
			Timeout:   timeout,
		}
		ehttp.Instrument(client)
	})
	return client
}

func getHTTPTransport(
	maxIdleConns, maxIdleConnsPerHost int,
) http.RoundTripper {
	transport, ok := http.DefaultTransport.(*http.Transport)
	if !ok {
		return http.DefaultTransport // tests use mock transport
	}

	transport.MaxIdleConnsPerHost = maxIdleConnsPerHost
	if maxIdleConns > 0 {
		transport.MaxIdleConns = maxIdleConns
	}

	return transport
}

// NewKhan returns a new khan API application
func NewKhan(config *viper.Viper) KhanInterface {
	config.SetDefault("khan.timeout", 500*time.Millisecond)
	config.SetDefault("khan.maxIdleConnsPerHost", http.DefaultMaxIdleConnsPerHost)

	k := &Khan{
		httpClient: getHTTPClient(
			config.GetDuration("khan.timeout"),
			config.GetInt("khan.maxIdleConns"),
			config.GetInt("khan.maxIdleConnsPerHost"),
		),
		Config: config,
		url:    config.GetString("khan.url"),
		user:   config.GetString("khan.user"),
		pass:   config.GetString("khan.pass"),
		gameID: config.GetString("khan.gameid"),
	}
	return k
}

func (k *Khan) sendTo(ctx context.Context, method, url string, payload interface{}) ([]byte, error) {
	payloadJSON, err := json.Marshal(payload)
	if err != nil {
		return nil, err
	}

	var req *http.Request

	if payload != nil {
		req, err = http.NewRequest(method, url, bytes.NewBuffer(payloadJSON))
		if err != nil {
			return nil, err
		}
	} else {
		req, err = http.NewRequest(method, url, nil)
		if err != nil {
			return nil, err
		}
	}
	req.Header.Set("Content-Type", "application/json")
	req.SetBasicAuth(k.user, k.pass)
	if ctx == nil {
		ctx = context.Background()
	}
	req = req.WithContext(ctx)

	resp, err := k.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, respErr := ioutil.ReadAll(resp.Body)
	if respErr != nil {
		return nil, respErr
	}

	if resp.StatusCode > 399 {
		return nil, newRequestError(resp.StatusCode, string(body))
	}

	return body, nil
}

func (k *Khan) buildURL(pathname string) string {
	return fmt.Sprintf("%s/games/%s/%s", k.url, k.gameID, pathname)
}

func (k *Khan) buildCreatePlayerURL() string {
	pathname := "players"
	return k.buildURL(pathname)
}

func (k *Khan) buildUpdatePlayerURL(playerID string) string {
	pathname := fmt.Sprintf("players/%s", playerID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrievePlayerURL(playerID string) string {
	pathname := fmt.Sprintf("players/%s", playerID)
	return k.buildURL(pathname)
}

func (k *Khan) buildCreateClanURL() string {
	pathname := "clans"
	return k.buildURL(pathname)
}

func (k *Khan) buildUpdateClanURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrieveClanURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrieveClanSummaryURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/summary", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildRetrieveClansSummaryURL(clanIDs []string) string {
	pathname := fmt.Sprintf("clans-summary?clanPublicIds=%s", strings.Join(clanIDs, ","))
	return k.buildURL(pathname)
}

func (k *Khan) buildApplyForMembershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/application", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildInviteForMembershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/invitation", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildApproveDenyMembershipApplicationURL(clanID, action string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/application/%s", clanID, action)
	return k.buildURL(pathname)
}

func (k *Khan) buildApproveDenyMembershipInvitationURL(clanID, action string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/invitation/%s", clanID, action)
	return k.buildURL(pathname)
}

func (k *Khan) buildPromoteDemoteURL(clanID, action string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/%s", clanID, action)
	return k.buildURL(pathname)
}

func (k *Khan) buildDeleteMembershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/memberships/delete", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildLeaveClanURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/leave", clanID)
	return k.buildURL(pathname)
}

func (k *Khan) buildTransferOwnershipURL(clanID string) string {
	pathname := fmt.Sprintf("clans/%s/transfer-ownership", clanID)
	return k.buildURL(pathname)
}

// CreatePlayer calls Khan to create a new player
func (k *Khan) CreatePlayer(ctx context.Context, publicID, name string, metadata interface{}) (string, error) {
	route := k.buildCreatePlayerURL()
	playerPayload := &Player{
		PublicID: publicID,
		Name:     name,
		Metadata: metadata,
	}
	body, err := k.sendTo(ctx, "POST", route, playerPayload)

	if err != nil {
		return "", err
	}

	var player Player
	err = json.Unmarshal(body, &player)

	return player.PublicID, err
}

// UpdatePlayer calls khan to update the player
func (k *Khan) UpdatePlayer(
	ctx context.Context,
	publicID, name string,
	metadata interface{},
) (*Result, error) {
	route := k.buildUpdatePlayerURL(publicID)
	playerPayload := &Player{Name: name, Metadata: metadata}
	body, err := k.sendTo(ctx, "PUT", route, playerPayload)
	if err != nil {
		return nil, err
	}

	var result Result
	err = json.Unmarshal(body, &result)
	return &result, err
}

// RetrievePlayer calls the retrieve player route from khan
func (k *Khan) RetrievePlayer(ctx context.Context, publicID string) (*Player, error) {
	route := k.buildRetrievePlayerURL(publicID)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var player Player
	err = json.Unmarshal(body, &player)
	return &player, err
}

// CreateClan calls the create clan route from khan
func (k *Khan) CreateClan(ctx context.Context, clan *ClanPayload) (string, error) {
	route := k.buildCreateClanURL()
	body, err := k.sendTo(ctx, "POST", route, clan)

	if err != nil {
		return "", err
	}

	var player Player
	err = json.Unmarshal(body, &player)
	return player.PublicID, err
}

// UpdateClan calls the update clan route from khan
func (k *Khan) UpdateClan(ctx context.Context, clan *ClanPayload) (*Result, error) {
	route := k.buildUpdateClanURL(clan.PublicID)
	body, err := k.sendTo(ctx, "PUT", route, clan)
	if err != nil {
		return nil, err
	}

	var result Result
	err = json.Unmarshal(body, &result)
	return &result, err
}

// RetrieveClanSummary calls the route to retrieve clan summary from khan
func (k *Khan) RetrieveClanSummary(ctx context.Context, clanID string) (*ClanSummary, error) {
	route := k.buildRetrieveClanSummaryURL(clanID)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var clanSummary ClanSummary
	err = json.Unmarshal(body, &clanSummary)
	return &clanSummary, err
}

// RetrieveClansSummary calls the route to retrieve clans summary from khan
func (k *Khan) RetrieveClansSummary(ctx context.Context, clanIDs []string) ([]*ClanSummary, error) {
	route := k.buildRetrieveClansSummaryURL(clanIDs)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var clansSummary ClansSummary
	err = json.Unmarshal(body, &clansSummary)
	if err != nil {
		return nil, err
	}
	return clansSummary.Clans, nil
}

// RetrieveClan calls the route to retrieve clan from khan
func (k *Khan) RetrieveClan(ctx context.Context, clanID string) (*Clan, error) {
	route := k.buildRetrieveClanURL(clanID)
	body, err := k.sendTo(ctx, "GET", route, nil)

	if err != nil {
		return nil, err
	}

	var clan Clan
	err = json.Unmarshal(body, &clan)
	return &clan, err
}

// ApplyForMembership calls apply for membership route on khan
func (k *Khan) ApplyForMembership(
	ctx context.Context,
	payload *ApplicationPayload,
) (*ClanApplyResult, error) {
	route := k.buildApplyForMembershipURL(payload.ClanID)
	body, err := k.sendTo(ctx, "POST", route, payload)

	if err != nil {
		return nil, err
	}

	var application ClanApplyResult
	err = json.Unmarshal(body, &application)
	return &application, err
}

// InviteForMembership invites a clan member to join clan
func (k *Khan) InviteForMembership(
	ctx context.Context,
	payload *InvitationPayload,
) (*Result, error) {
	route := k.buildInviteForMembershipURL(payload.ClanID)
	return k.defaultPostRequest(ctx, route, payload)
}

// ApproveDenyMembershipApplication approves or deny player
// application on clan
func (k *Khan) ApproveDenyMembershipApplication(
	ctx context.Context,
	payload *ApplicationApprovalPayload,
) (*Result, error) {
	route := k.buildApproveDenyMembershipApplicationURL(payload.ClanID, payload.Action)
	return k.defaultPostRequest(ctx, route, payload)
}

// ApproveDenyMembershipInvitation approves or deny player
// invitation on clan
func (k *Khan) ApproveDenyMembershipInvitation(
	ctx context.Context,
	payload *InvitationApprovalPayload,
) (*Result, error) {
	route := k.buildApproveDenyMembershipInvitationURL(payload.ClanID, payload.Action)
	return k.defaultPostRequest(ctx, route, payload)
}

// PromoteDemote promotes or demotes player on clan
func (k *Khan) PromoteDemote(
	ctx context.Context,
	payload *PromoteDemotePayload,
) (*Result, error) {
	route := k.buildPromoteDemoteURL(payload.ClanID, payload.Action)
	return k.defaultPostRequest(ctx, route, payload)
}

// DeleteMembership deletes membership on clan
func (k *Khan) DeleteMembership(
	ctx context.Context,
	payload *DeleteMembershipPayload,
) (*Result, error) {
	route := k.buildDeleteMembershipURL(payload.ClanID)
	return k.defaultPostRequest(ctx, route, payload)
}

// LeaveClan allows member to leave clan
func (k *Khan) LeaveClan(
	ctx context.Context,
	clanID string,
) (*LeaveClanResult, error) {
	route := k.buildLeaveClanURL(clanID)
	body, err := k.sendTo(ctx, "POST", route, nil)

	if err != nil {
		return nil, err
	}

	var result LeaveClanResult
	err = json.Unmarshal(body, &result)
	return &result, err
}

// TransferOwnership transfers clan ownership to another member
func (k *Khan) TransferOwnership(
	ctx context.Context,
	playerPublicID, clanID string,
) (*TransferOwnershipResult, error) {
	route := k.buildTransferOwnershipURL(clanID)
	body, err := k.sendTo(ctx, "POST", route, map[string]interface{}{
		"playerPublicID": playerPublicID,
	})

	if err != nil {
		return nil, err
	}

	var result TransferOwnershipResult
	err = json.Unmarshal(body, &result)
	return &result, err
}

func (k *Khan) defaultPostRequest(
	ctx context.Context,
	route string,
	payload interface{},
) (*Result, error) {
	body, err := k.sendTo(ctx, "POST", route, payload)
	if err != nil {
		return nil, err
	}

	var result Result
	err = json.Unmarshal(body, &result)
	return &result, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

type wstcg struct{}

func (e wstcg) getCardDeckInfo(url string) ([]Card, error) {
	var cardsDeck = []Card{}
	return cardsDeck, nil
}

func (e wstcg) getCardDecksInfoList(url string) ([][]Card, error) {
	var decks = [][]Card{}

	doc, err := goquery.NewDocument(url)
	if err != nil {
		fmt.Println(err)
		return decks, err
	}

	doc.Find("table.deckrecipe_table").Each(func(i int, s *goquery.Selection) {
		cardsDeck := []Card{}
		s.Find("tr.kind_2").Each(func(i int, s *goquery.Selection) {
			card := Card{}
			cardID := s.Find(".cardno").Text()
			if cardID != "" {
				var split = strings.Split(cardID, "-")
				card.ID = fmt.Sprintf("%s%s%s", split[0], "-", strings.Replace(split[1], "E", "", 1))
			} else {
				return
			}

			cardAmount := s.Find(".cardnum").Text()
			if cardAmount != "" {
				cardAmount = strings.TrimSpace(strings.TrimRight(cardAmount, "枚"))
				// Japanese bullshit '４' != '4'
				jpwhut := string(fmt.Sprintf("%+q", cardAmount))
				r := string(jpwhut[len(jpwhut)-2])
				card.Amount, err = strconv.Atoi(r)
				if err != nil {
					fmt.Println(err)
				}
			}
			cardsDeck = append(cardsDeck, card)
		})
		decks = append(decks, cardsDeck)
	})
	return decks, nil
}

func (e wstcg) isMine(url string) bool {
	return strings.Contains(url, "ws-tcg")
}
<commit_msg>fix: get cx for wstcg<commit_after>package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

type wstcg struct{}

func (e wstcg) getCardDeckInfo(url string) ([]Card, error) {
	var cardsDeck = []Card{}
	return cardsDeck, nil
}

func (e wstcg) getCardDecksInfoList(url string) ([][]Card, error) {
	var decks = [][]Card{}

	doc, err := goquery.NewDocument(url)
	if err != nil {
		fmt.Println(err)
		return decks, err
	}

	doc.Find("table.deckrecipe_table").Each(func(i int, s *goquery.Selection) {
		cardsDeck := []Card{}
		s.Find("tr").Each(func(i int, s *goquery.Selection) {
			card := Card{}
			cardID := s.Find(".cardno").Text()
			if cardID != "" {
				var split = strings.Split(cardID, "-")
				card.ID = fmt.Sprintf("%s%s%s", split[0], "-", strings.Replace(split[1], "E", "", 1))
			} else {
				return
			}

			cardAmount := s.Find(".cardnum").Text()
			if cardAmount != "" {
				cardAmount = strings.TrimSpace(strings.TrimRight(cardAmount, "枚"))
				// Japanese bullshit '４' != '4'
				jpwhut := string(fmt.Sprintf("%+q", cardAmount))
				r := string(jpwhut[len(jpwhut)-2])
				card.Amount, err = strconv.Atoi(r)
				if err != nil {
					fmt.Println(err)
				}
			}
			cardsDeck = append(cardsDeck, card)
		})
		decks = append(decks, cardsDeck)
	})
	return decks, nil
}

func (e wstcg) isMine(url string) bool {
	return strings.Contains(url, "ws-tcg")
}
<|endoftext|>


<commit_before>// Copyright 2021 Prometheus Team
// 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 sns

import (
	"context"
	"net/http"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/go-kit/kit/log"
	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/alertmanager/types"
	commoncfg "github.com/prometheus/common/config"
)

// Notifier implements a Notifier for SNS notifications.
type Notifier struct {
	conf    *config.SNSConfig
	tmpl    *template.Template
	logger  log.Logger
	client  *http.Client
	retrier *notify.Retrier
}

func (n Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
	// TODO: get Credentials from env variables
	creds := credentials.NewStaticCredentials(n.conf.Sigv4.AccessKey, string(n.conf.Sigv4.SecretKey), "")

	sess, err := session.NewSessionWithOptions(session.Options{
		Config: aws.Config{
			Region:      aws.String(n.conf.Sigv4.Region),
			Credentials: creds,
			Endpoint:    aws.String(n.conf.APIUrl),
		},
		Profile: n.conf.Sigv4.Profile,
	})

	data := notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
	tmpl := notify.TmplText(n.tmpl, data, &err)
	message := tmpl(n.conf.Message)

	client := sns.New(sess)
	publishInput := &sns.PublishInput{}

	if n.conf.TopicARN != "" {
		publishInput.SetTopicArn(n.conf.TopicARN)
		// TODO: Truncate for SNS at 256KB
		publishInput.SetMessage(message)
	}
	if n.conf.PhoneNumber != "" {
		publishInput.SetPhoneNumber(n.conf.PhoneNumber)
		// Truncate for SMS
		trunc, isTruncated := notify.Truncate(message, 140)
		if isTruncated {
			publishInput.SetMessage(trunc)
		} else {
			publishInput.SetMessage(message)
		}
	}
	if n.conf.TopicARN != "" {
		publishInput.SetTopicArn(n.conf.TopicARN)
		// TODO: Truncate for SNS at 256KB
		publishInput.SetMessage(message)
	}

	if len(n.conf.Attributes) > 0 {
		attributes := map[string]*sns.MessageAttributeValue{}
		for k, v := range n.conf.Attributes {
			attributes[k] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(v)}
		}
		publishInput.SetMessageAttributes(attributes)
	}

	if n.conf.Subject != "" {
		publishInput.SetSubject(n.conf.Subject)
	}

	publishOutput, err := client.Publish(publishInput)
	if err != nil {
		// AWS Response is bad, probably a config issue
		return false, err
	}

	err = n.logger.Log(publishOutput.String())
	if err != nil {
		return false, err
	}

	// Response is good and does not need to be retried
	return false, nil
}

// New returns a new SNS notification handler.
func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {

	client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", append(httpOpts, commoncfg.WithHTTP2Disabled())...)
	if err != nil {
		return nil, err
	}

	return &Notifier{
		conf:    c,
		tmpl:    t,
		logger:  l,
		client:  client,
		retrier: &notify.Retrier{},
	}, nil
}
<commit_msg>ARN Auth start<commit_after>// Copyright 2021 Prometheus Team
// 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 sns

import (
	"context"
	"net/http"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/go-kit/kit/log"
	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/alertmanager/types"
	commoncfg "github.com/prometheus/common/config"
)

// Notifier implements a Notifier for SNS notifications.
type Notifier struct {
	conf    *config.SNSConfig
	tmpl    *template.Template
	logger  log.Logger
	client  *http.Client
	retrier *notify.Retrier
}

func (n Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
	// TODO: get Credentials from env variables if none passed in + api auth
	creds := credentials.NewStaticCredentials(n.conf.Sigv4.AccessKey, string(n.conf.Sigv4.SecretKey), "")
	if n.conf.Sigv4.AccessKey == "" {
		creds = nil
	}

	sess, err := session.NewSessionWithOptions(session.Options{
		Config: aws.Config{
			CredentialsChainVerboseErrors: aws.Bool(true),
			Region:      aws.String(n.conf.Sigv4.Region),
			Credentials: creds,
			Endpoint:    aws.String(n.conf.APIUrl),
		},
		Profile: n.conf.Sigv4.Profile,
	})

	if n.conf.Sigv4.RoleARN != "" {
		sess.Config.Credentials = stscreds.NewCredentials(sess, n.conf.Sigv4.RoleARN)
	}

	data := notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
	tmpl := notify.TmplText(n.tmpl, data, &err)
	message := tmpl(n.conf.Message)

	client := sns.New(sess, &aws.Config{Credentials: creds})
	publishInput := &sns.PublishInput{}

	if n.conf.TopicARN != "" {
		publishInput.SetTopicArn(n.conf.TopicARN)
		// TODO: Truncate for SNS at 256KB
		publishInput.SetMessage(message)
	}
	if n.conf.PhoneNumber != "" {
		publishInput.SetPhoneNumber(n.conf.PhoneNumber)
		// Truncate for SMS
		trunc, isTruncated := notify.Truncate(message, 140)
		if isTruncated {
			publishInput.SetMessage(trunc)
		} else {
			publishInput.SetMessage(message)
		}
	}
	if n.conf.TopicARN != "" {
		publishInput.SetTopicArn(n.conf.TopicARN)
		// TODO: Truncate for SNS at 256KB
		publishInput.SetMessage(message)
	}

	if len(n.conf.Attributes) > 0 {
		attributes := map[string]*sns.MessageAttributeValue{}
		for k, v := range n.conf.Attributes {
			attributes[k] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(v)}
		}
		publishInput.SetMessageAttributes(attributes)
	}

	if n.conf.Subject != "" {
		publishInput.SetSubject(n.conf.Subject)
	}

	publishOutput, err := client.Publish(publishInput)
	if err != nil {
		// AWS Response is bad, probably a config issue
		return false, err
	}

	err = n.logger.Log(publishOutput.String())
	if err != nil {
		return false, err
	}

	// Response is good and does not need to be retried
	return false, nil
}

// New returns a new SNS notification handler.
func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {

	client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", append(httpOpts, commoncfg.WithHTTP2Disabled())...)
	if err != nil {
		return nil, err
	}

	return &Notifier{
		conf:    c,
		tmpl:    t,
		logger:  l,
		client:  client,
		retrier: &notify.Retrier{},
	}, nil
}
<|endoftext|>


<commit_before>package readline

import (
	"context"
	"io"
	"strings"
	"unicode"

	"github.com/atotto/clipboard"
)

func keyFuncEnter(ctx context.Context, this *Buffer) Result { // Ctrl-M
	return ENTER
}

func keyFuncIntr(ctx context.Context, this *Buffer) Result { // Ctrl-C
	this.Length = 0
	this.Cursor = 0
	this.ViewStart = 0
	return INTR
}

func keyFuncHead(ctx context.Context, this *Buffer) Result { // Ctrl-A
	this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
	this.Cursor = 0
	this.ViewStart = 0
	this.Repaint(0, 1)
	return CONTINUE
}

func keyFuncBackward(ctx context.Context, this *Buffer) Result { // Ctrl-B
	if this.Cursor <= 0 {
		return CONTINUE
	}
	this.Cursor--
	if this.Cursor < this.ViewStart {
		this.ViewStart--
		this.Repaint(this.Cursor, 1)
	} else {
		this.Backspace(GetCharWidth(this.Buffer[this.Cursor]))
	}
	return CONTINUE
}

func keyFuncTail(ctx context.Context, this *Buffer) Result { // Ctrl-E
	allength := this.GetWidthBetween(this.ViewStart, this.Length)
	if allength < this.ViewWidth() {
		for ; this.Cursor < this.Length; this.Cursor++ {
			this.PutRune(this.Buffer[this.Cursor])
		}
	} else {
		io.WriteString(this.Out, "\a")
		this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
		this.ViewStart = this.Length - 1
		w := GetCharWidth(this.Buffer[this.ViewStart])
		for {
			if this.ViewStart <= 0 {
				break
			}
			w_ := w + GetCharWidth(this.Buffer[this.ViewStart-1])
			if w_ >= this.ViewWidth() {
				break
			}
			w = w_
			this.ViewStart--
		}
		for this.Cursor = this.ViewStart; this.Cursor < this.Length; this.Cursor++ {
			this.PutRune(this.Buffer[this.Cursor])
		}
	}
	return CONTINUE
}

func keyFuncForward(ctx context.Context, this *Buffer) Result { // Ctrl-F
	if this.Cursor >= this.Length {
		return CONTINUE
	}
	w := this.GetWidthBetween(this.ViewStart, this.Cursor+1)
	if w < this.ViewWidth() {
		// No Scroll
		this.PutRune(this.Buffer[this.Cursor])
	} else {
		// Right Scroll
		this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
		if GetCharWidth(this.Buffer[this.Cursor]) > GetCharWidth(this.Buffer[this.ViewStart]) {
			this.ViewStart++
		}
		this.ViewStart++
		for i := this.ViewStart; i <= this.Cursor; i++ {
			this.PutRune(this.Buffer[i])
		}
		this.Eraseline()
	}
	this.Cursor++
	return CONTINUE
}

func keyFuncBackSpace(ctx context.Context, this *Buffer) Result { // Backspace
	if this.Cursor > 0 {
		this.Cursor--
		delw := this.Delete(this.Cursor, 1)
		if this.Cursor >= this.ViewStart {
			this.Backspace(delw)
		} else {
			this.ViewStart = this.Cursor
		}
		this.Repaint(this.Cursor, delw)
	}
	return CONTINUE
}

func keyFuncDelete(ctx context.Context, this *Buffer) Result { // Del
	delw := this.Delete(this.Cursor, 1)
	this.Repaint(this.Cursor, delw)
	return CONTINUE
}

func keyFuncDeleteOrAbort(ctx context.Context, this *Buffer) Result { // Ctrl-D
	if this.Length > 0 {
		return keyFuncDelete(ctx, this)
	} else {
		return ABORT
	}
}

func keyFuncInsertSelf(ctx context.Context, this *Buffer, keys string) Result {
	if len(keys) == 2 && keys[0] == '\x1B' { // for AltGr-shift
		keys = keys[1:]
	}
	this.Insert(this.Cursor, []rune(keys))

	w := this.GetWidthBetween(this.ViewStart, this.Cursor)
	w1 := GetStringWidth(keys)
	if w+w1 >= this.ViewWidth() {
		// scroll left
		this.Backspace(w)
		this.Cursor += len(keys)
		this.ResetViewStart()
		for i := this.ViewStart; i < this.Cursor; i++ {
			this.PutRune(this.Buffer[i])
		}
		this.Eraseline()
	} else {
		this.Repaint(this.Cursor, -w1)
		this.Cursor += len(keys)
	}
	return CONTINUE
}

func keyFuncClearAfter(ctx context.Context, this *Buffer) Result {
	clipboard.WriteAll(this.SubString(this.Cursor, this.Length))

	this.Eraseline()
	this.Length = this.Cursor
	return CONTINUE
}

func keyFuncClear(ctx context.Context, this *Buffer) Result {
	width := this.GetWidthBetween(this.ViewStart, this.Cursor)
	this.Backspace(width)
	this.Eraseline()
	this.Length = 0
	this.Cursor = 0
	this.ViewStart = 0
	return CONTINUE
}

func keyFuncWordRubout(ctx context.Context, this *Buffer) Result {
	org_cursor := this.Cursor
	for this.Cursor > 0 && unicode.IsSpace(this.Buffer[this.Cursor-1]) {
		this.Cursor--
	}
	i := this.CurrentWordTop()
	clipboard.WriteAll(this.SubString(i, org_cursor))
	keta := this.Delete(i, org_cursor-i)
	if i >= this.ViewStart {
		this.Backspace(keta)
	} else {
		this.Backspace(this.GetWidthBetween(this.ViewStart, org_cursor))
	}
	this.Cursor = i
	this.Repaint(i, keta)
	return CONTINUE
}

func keyFuncClearBefore(ctx context.Context, this *Buffer) Result {
	keta := this.GetWidthBetween(this.ViewStart, this.Cursor)
	clipboard.WriteAll(this.SubString(0, this.Cursor))
	this.Delete(0, this.Cursor)
	this.Backspace(keta)
	this.Cursor = 0
	this.ViewStart = 0
	this.Repaint(0, keta)
	return CONTINUE
}

func keyFuncCLS(ctx context.Context, this *Buffer) Result {
	io.WriteString(this.Out, "\x1B[1;1H\x1B[2J")
	this.RepaintAll()
	return CONTINUE
}

func keyFuncRepaintOnNewline(ctx context.Context, this *Buffer) Result {
	this.Out.WriteByte('\n')
	this.RepaintAll()
	return CONTINUE
}

func keyFuncQuotedInsert(ctx context.Context, this *Buffer) Result {
	io.WriteString(this.Out, ansiCursorOn)
	defer io.WriteString(this.Out, ansiCursorOff)

	this.Out.Flush()
	if key, err := getKey(this.TTY); err == nil {
		return keyFuncInsertSelf(ctx, this, key)
	} else {
		return CONTINUE
	}
}

func keyFuncPaste(ctx context.Context, this *Buffer) Result {
	text, err := clipboard.ReadAll()
	if err != nil {
		return CONTINUE
	}
	this.InsertAndRepaint(text)
	return CONTINUE
}

func keyFuncPasteQuote(ctx context.Context, this *Buffer) Result {
	text, err := clipboard.ReadAll()
	if err != nil {
		return CONTINUE
	}
	if strings.IndexRune(text, ' ') >= 0 &&
		!strings.HasPrefix(text, `"`) {
		text = `"` + strings.Replace(text, `"`, `""`, -1) + `"`
	}
	this.InsertAndRepaint(text)
	return CONTINUE
}

func maxInt(a, b int) int {
	if a < b {
		return b
	} else {
		return a
	}
}

func keyFuncSwapChar(ctx context.Context, this *Buffer) Result {
	if this.Length == this.Cursor {
		if this.Cursor < 2 {
			return CONTINUE
		}
		this.Buffer[this.Cursor-2], this.Buffer[this.Cursor-1] = this.Buffer[this.Cursor-1], this.Buffer[this.Cursor-2]

		redrawStart := maxInt(this.Cursor-2, this.ViewStart)
		this.Backspace(this.GetWidthBetween(redrawStart, this.Cursor))
		for i := redrawStart; i < this.Cursor; i++ {
			this.PutRune(this.Buffer[i])
		}
	} else {
		if this.Cursor < 1 {
			return CONTINUE
		}

		w := this.GetWidthBetween(this.ViewStart, this.Cursor+1)
		this.Buffer[this.Cursor-1], this.Buffer[this.Cursor] = this.Buffer[this.Cursor], this.Buffer[this.Cursor-1]
		if w >= this.ViewWidth() {
			// cursor move right and scroll
			w_1 := w - GetCharWidth(this.Buffer[this.Cursor])
			this.Backspace(w_1)
			this.ViewStart++
			for i := this.ViewStart; i <= this.Cursor; i++ {
				this.PutRune(this.Buffer[i])
			}
		} else {
			// no necessary to scroll
			redrawStart := maxInt(this.Cursor-1, this.ViewStart)
			this.Backspace(this.GetWidthBetween(redrawStart, this.Cursor))
			for i := redrawStart; i <= this.Cursor; i++ {
				this.PutRune(this.Buffer[i])
			}
		}
		this.Cursor++
	}
	return CONTINUE
}

func keyFuncBackwardWord(ctx context.Context, this *Buffer) Result {
	newPos := this.Cursor
	for newPos > 0 && this.Buffer[newPos-1] == ' ' {
		newPos--
	}
	for newPos > 0 && this.Buffer[newPos-1] != ' ' {
		newPos--
	}
	if newPos >= this.ViewStart {
		w := this.GetWidthBetween(newPos, this.Cursor)
		this.Backspace(w)
		this.Cursor = newPos
	} else {
		w := this.GetWidthBetween(this.ViewStart, this.Cursor)
		this.Backspace(w)
		this.Cursor = newPos
		this.ViewStart = newPos
		this.Repaint(newPos, 0)
	}
	return CONTINUE
}

func keyFuncForwardWord(ctx context.Context, this *Buffer) Result {
	newPos := this.Cursor
	for newPos < this.Length && this.Buffer[newPos] != ' ' {
		newPos++
	}
	for newPos < this.Length && this.Buffer[newPos] == ' ' {
		newPos++
	}
	w := this.GetWidthBetween(this.ViewStart, newPos)
	if w < this.ViewWidth() {
		for this.Cursor < newPos {
			this.PutRune(this.Buffer[this.Cursor])
			this.Cursor++
		}
	} else {
		this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
		this.Cursor = newPos
		for w >= this.ViewWidth() {
			w -= GetCharWidth(this.Buffer[this.ViewStart])
			this.ViewStart++
		}
		for p := this.ViewStart; p < this.Cursor; p++ {
			this.PutRune(this.Buffer[p])
		}
		this.Eraseline()
	}
	return CONTINUE
}
<commit_msg>Fix: readline: wrong count for Japanese Letter<commit_after>package readline

import (
	"context"
	"io"
	"strings"
	"unicode"

	"github.com/atotto/clipboard"
)

func keyFuncEnter(ctx context.Context, this *Buffer) Result { // Ctrl-M
	return ENTER
}

func keyFuncIntr(ctx context.Context, this *Buffer) Result { // Ctrl-C
	this.Length = 0
	this.Cursor = 0
	this.ViewStart = 0
	return INTR
}

func keyFuncHead(ctx context.Context, this *Buffer) Result { // Ctrl-A
	this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
	this.Cursor = 0
	this.ViewStart = 0
	this.Repaint(0, 1)
	return CONTINUE
}

func keyFuncBackward(ctx context.Context, this *Buffer) Result { // Ctrl-B
	if this.Cursor <= 0 {
		return CONTINUE
	}
	this.Cursor--
	if this.Cursor < this.ViewStart {
		this.ViewStart--
		this.Repaint(this.Cursor, 1)
	} else {
		this.Backspace(GetCharWidth(this.Buffer[this.Cursor]))
	}
	return CONTINUE
}

func keyFuncTail(ctx context.Context, this *Buffer) Result { // Ctrl-E
	allength := this.GetWidthBetween(this.ViewStart, this.Length)
	if allength < this.ViewWidth() {
		for ; this.Cursor < this.Length; this.Cursor++ {
			this.PutRune(this.Buffer[this.Cursor])
		}
	} else {
		io.WriteString(this.Out, "\a")
		this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
		this.ViewStart = this.Length - 1
		w := GetCharWidth(this.Buffer[this.ViewStart])
		for {
			if this.ViewStart <= 0 {
				break
			}
			w_ := w + GetCharWidth(this.Buffer[this.ViewStart-1])
			if w_ >= this.ViewWidth() {
				break
			}
			w = w_
			this.ViewStart--
		}
		for this.Cursor = this.ViewStart; this.Cursor < this.Length; this.Cursor++ {
			this.PutRune(this.Buffer[this.Cursor])
		}
	}
	return CONTINUE
}

func keyFuncForward(ctx context.Context, this *Buffer) Result { // Ctrl-F
	if this.Cursor >= this.Length {
		return CONTINUE
	}
	w := this.GetWidthBetween(this.ViewStart, this.Cursor+1)
	if w < this.ViewWidth() {
		// No Scroll
		this.PutRune(this.Buffer[this.Cursor])
	} else {
		// Right Scroll
		this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
		if GetCharWidth(this.Buffer[this.Cursor]) > GetCharWidth(this.Buffer[this.ViewStart]) {
			this.ViewStart++
		}
		this.ViewStart++
		for i := this.ViewStart; i <= this.Cursor; i++ {
			this.PutRune(this.Buffer[i])
		}
		this.Eraseline()
	}
	this.Cursor++
	return CONTINUE
}

func keyFuncBackSpace(ctx context.Context, this *Buffer) Result { // Backspace
	if this.Cursor > 0 {
		this.Cursor--
		delw := this.Delete(this.Cursor, 1)
		if this.Cursor >= this.ViewStart {
			this.Backspace(delw)
		} else {
			this.ViewStart = this.Cursor
		}
		this.Repaint(this.Cursor, delw)
	}
	return CONTINUE
}

func keyFuncDelete(ctx context.Context, this *Buffer) Result { // Del
	delw := this.Delete(this.Cursor, 1)
	this.Repaint(this.Cursor, delw)
	return CONTINUE
}

func keyFuncDeleteOrAbort(ctx context.Context, this *Buffer) Result { // Ctrl-D
	if this.Length > 0 {
		return keyFuncDelete(ctx, this)
	} else {
		return ABORT
	}
}

func keyFuncInsertSelf(ctx context.Context, this *Buffer, keys string) Result {
	if len(keys) == 2 && keys[0] == '\x1B' { // for AltGr-shift
		keys = keys[1:]
	}
	this.Insert(this.Cursor, []rune(keys))

	w := this.GetWidthBetween(this.ViewStart, this.Cursor)
	w1 := GetStringWidth(keys)
	if w+w1 >= this.ViewWidth() {
		// scroll left
		this.Backspace(w)
		this.Cursor += len([]rune(keys))
		this.ResetViewStart()
		for i := this.ViewStart; i < this.Cursor; i++ {
			this.PutRune(this.Buffer[i])
		}
		this.Eraseline()
	} else {
		this.Repaint(this.Cursor, -w1)
		this.Cursor += len([]rune(keys))
	}
	return CONTINUE
}

func keyFuncClearAfter(ctx context.Context, this *Buffer) Result {
	clipboard.WriteAll(this.SubString(this.Cursor, this.Length))

	this.Eraseline()
	this.Length = this.Cursor
	return CONTINUE
}

func keyFuncClear(ctx context.Context, this *Buffer) Result {
	width := this.GetWidthBetween(this.ViewStart, this.Cursor)
	this.Backspace(width)
	this.Eraseline()
	this.Length = 0
	this.Cursor = 0
	this.ViewStart = 0
	return CONTINUE
}

func keyFuncWordRubout(ctx context.Context, this *Buffer) Result {
	org_cursor := this.Cursor
	for this.Cursor > 0 && unicode.IsSpace(this.Buffer[this.Cursor-1]) {
		this.Cursor--
	}
	i := this.CurrentWordTop()
	clipboard.WriteAll(this.SubString(i, org_cursor))
	keta := this.Delete(i, org_cursor-i)
	if i >= this.ViewStart {
		this.Backspace(keta)
	} else {
		this.Backspace(this.GetWidthBetween(this.ViewStart, org_cursor))
	}
	this.Cursor = i
	this.Repaint(i, keta)
	return CONTINUE
}

func keyFuncClearBefore(ctx context.Context, this *Buffer) Result {
	keta := this.GetWidthBetween(this.ViewStart, this.Cursor)
	clipboard.WriteAll(this.SubString(0, this.Cursor))
	this.Delete(0, this.Cursor)
	this.Backspace(keta)
	this.Cursor = 0
	this.ViewStart = 0
	this.Repaint(0, keta)
	return CONTINUE
}

func keyFuncCLS(ctx context.Context, this *Buffer) Result {
	io.WriteString(this.Out, "\x1B[1;1H\x1B[2J")
	this.RepaintAll()
	return CONTINUE
}

func keyFuncRepaintOnNewline(ctx context.Context, this *Buffer) Result {
	this.Out.WriteByte('\n')
	this.RepaintAll()
	return CONTINUE
}

func keyFuncQuotedInsert(ctx context.Context, this *Buffer) Result {
	io.WriteString(this.Out, ansiCursorOn)
	defer io.WriteString(this.Out, ansiCursorOff)

	this.Out.Flush()
	if key, err := getKey(this.TTY); err == nil {
		return keyFuncInsertSelf(ctx, this, key)
	} else {
		return CONTINUE
	}
}

func keyFuncPaste(ctx context.Context, this *Buffer) Result {
	text, err := clipboard.ReadAll()
	if err != nil {
		return CONTINUE
	}
	this.InsertAndRepaint(text)
	return CONTINUE
}

func keyFuncPasteQuote(ctx context.Context, this *Buffer) Result {
	text, err := clipboard.ReadAll()
	if err != nil {
		return CONTINUE
	}
	if strings.IndexRune(text, ' ') >= 0 &&
		!strings.HasPrefix(text, `"`) {
		text = `"` + strings.Replace(text, `"`, `""`, -1) + `"`
	}
	this.InsertAndRepaint(text)
	return CONTINUE
}

func maxInt(a, b int) int {
	if a < b {
		return b
	} else {
		return a
	}
}

func keyFuncSwapChar(ctx context.Context, this *Buffer) Result {
	if this.Length == this.Cursor {
		if this.Cursor < 2 {
			return CONTINUE
		}
		this.Buffer[this.Cursor-2], this.Buffer[this.Cursor-1] = this.Buffer[this.Cursor-1], this.Buffer[this.Cursor-2]

		redrawStart := maxInt(this.Cursor-2, this.ViewStart)
		this.Backspace(this.GetWidthBetween(redrawStart, this.Cursor))
		for i := redrawStart; i < this.Cursor; i++ {
			this.PutRune(this.Buffer[i])
		}
	} else {
		if this.Cursor < 1 {
			return CONTINUE
		}

		w := this.GetWidthBetween(this.ViewStart, this.Cursor+1)
		this.Buffer[this.Cursor-1], this.Buffer[this.Cursor] = this.Buffer[this.Cursor], this.Buffer[this.Cursor-1]
		if w >= this.ViewWidth() {
			// cursor move right and scroll
			w_1 := w - GetCharWidth(this.Buffer[this.Cursor])
			this.Backspace(w_1)
			this.ViewStart++
			for i := this.ViewStart; i <= this.Cursor; i++ {
				this.PutRune(this.Buffer[i])
			}
		} else {
			// no necessary to scroll
			redrawStart := maxInt(this.Cursor-1, this.ViewStart)
			this.Backspace(this.GetWidthBetween(redrawStart, this.Cursor))
			for i := redrawStart; i <= this.Cursor; i++ {
				this.PutRune(this.Buffer[i])
			}
		}
		this.Cursor++
	}
	return CONTINUE
}

func keyFuncBackwardWord(ctx context.Context, this *Buffer) Result {
	newPos := this.Cursor
	for newPos > 0 && this.Buffer[newPos-1] == ' ' {
		newPos--
	}
	for newPos > 0 && this.Buffer[newPos-1] != ' ' {
		newPos--
	}
	if newPos >= this.ViewStart {
		w := this.GetWidthBetween(newPos, this.Cursor)
		this.Backspace(w)
		this.Cursor = newPos
	} else {
		w := this.GetWidthBetween(this.ViewStart, this.Cursor)
		this.Backspace(w)
		this.Cursor = newPos
		this.ViewStart = newPos
		this.Repaint(newPos, 0)
	}
	return CONTINUE
}

func keyFuncForwardWord(ctx context.Context, this *Buffer) Result {
	newPos := this.Cursor
	for newPos < this.Length && this.Buffer[newPos] != ' ' {
		newPos++
	}
	for newPos < this.Length && this.Buffer[newPos] == ' ' {
		newPos++
	}
	w := this.GetWidthBetween(this.ViewStart, newPos)
	if w < this.ViewWidth() {
		for this.Cursor < newPos {
			this.PutRune(this.Buffer[this.Cursor])
			this.Cursor++
		}
	} else {
		this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))
		this.Cursor = newPos
		for w >= this.ViewWidth() {
			w -= GetCharWidth(this.Buffer[this.ViewStart])
			this.ViewStart++
		}
		for p := this.ViewStart; p < this.Cursor; p++ {
			this.PutRune(this.Buffer[p])
		}
		this.Eraseline()
	}
	return CONTINUE
}
<|endoftext|>


<commit_before>package server

import (
	//	"github.com/sogko/slumber/controllers"
	"github.com/sogko/slumber/domain"
	"net/http"
)

const defaultForbiddenAccessMessage = "Forbidden (403)"

type ErrorResponse struct {
	Message string `json:"message,omitempty"`
	Success bool   `json:"success"`
}

// TODO: Currently, AccessController only acts as a gateway for endpoints on router level. Build AC to handler other aspects of ACL
func NewAccessController(ctx domain.IContext, renderer domain.IRenderer) *AccessController {
	return &AccessController{domain.ACLMap{}, ctx, renderer}
}

// implements IAccessController
type AccessController struct {
	ACLMap   domain.ACLMap
	ctx      domain.IContext
	renderer domain.IRenderer
}

func (ac *AccessController) Add(aclMap *domain.ACLMap) {
	ac.ACLMap = ac.ACLMap.Append(aclMap)
}

func (ac *AccessController) AddHandler(action string, handler domain.ACLHandlerFunc) {
	ac.ACLMap[action] = handler
}

func (ac *AccessController) HasAction(action string) bool {
	fn := ac.ACLMap[action]
	return (fn != nil)
}

func (ac *AccessController) IsHTTPRequestAuthorized(req *http.Request, ctx domain.IContext, action string, user domain.IUser) (bool, string) {
	fn := ac.ACLMap[action]
	if fn == nil {
		// by default, if acl action/handler is not defined, request is not authorized
		return false, defaultForbiddenAccessMessage
	}

	result, message := fn(req, user)
	if message == "" {
		message = defaultForbiddenAccessMessage
	}
	return result, message
}

func (ac *AccessController) NewContextHandler(action string, next http.HandlerFunc) http.HandlerFunc {
	//func (ac *AccessController) NewHandler(action string, next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		user := ac.ctx.GetCurrentUserCtx(req)
		// `user` might be `nil` if has not authenticated.
		// ACL might want to allow anonymous / non-authenticated access (for login, e.g)

		result, message := ac.IsHTTPRequestAuthorized(req, ac.ctx, action, user)
		if !result {
			ac.renderer.Render(w, req, http.StatusForbidden, ErrorResponse{
				Message: message,
				Success: false,
			})
			return
		}

		next(w, req)
	}
}
<commit_msg>Set default OK ACL Message<commit_after>package server

import (
	//	"github.com/sogko/slumber/controllers"
	"github.com/sogko/slumber/domain"
	"net/http"
)

const defaultForbiddenAccessMessage = "Forbidden (403)"
const defaultOKAccessMessage = "OK"

type ErrorResponse struct {
	Message string `json:"message,omitempty"`
	Success bool   `json:"success"`
}

// TODO: Currently, AccessController only acts as a gateway for endpoints on router level. Build AC to handler other aspects of ACL
func NewAccessController(ctx domain.IContext, renderer domain.IRenderer) *AccessController {
	return &AccessController{domain.ACLMap{}, ctx, renderer}
}

// implements IAccessController
type AccessController struct {
	ACLMap   domain.ACLMap
	ctx      domain.IContext
	renderer domain.IRenderer
}

func (ac *AccessController) Add(aclMap *domain.ACLMap) {
	ac.ACLMap = ac.ACLMap.Append(aclMap)
}

func (ac *AccessController) AddHandler(action string, handler domain.ACLHandlerFunc) {
	ac.ACLMap[action] = handler
}

func (ac *AccessController) HasAction(action string) bool {
	fn := ac.ACLMap[action]
	return (fn != nil)
}

func (ac *AccessController) IsHTTPRequestAuthorized(req *http.Request, ctx domain.IContext, action string, user domain.IUser) (bool, string) {
	fn := ac.ACLMap[action]
	if fn == nil {
		// by default, if acl action/handler is not defined, request is not authorized
		return false, defaultForbiddenAccessMessage
	}

	result, message := fn(req, user)
	if result && message == "" {
		message = defaultOKAccessMessage
	}
	if !result && message == "" {
		message = defaultForbiddenAccessMessage
	}
	return result, message
}

func (ac *AccessController) NewContextHandler(action string, next http.HandlerFunc) http.HandlerFunc {
	//func (ac *AccessController) NewHandler(action string, next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		user := ac.ctx.GetCurrentUserCtx(req)
		// `user` might be `nil` if has not authenticated.
		// ACL might want to allow anonymous / non-authenticated access (for login, e.g)

		result, message := ac.IsHTTPRequestAuthorized(req, ac.ctx, action, user)
		if !result {
			ac.renderer.Render(w, req, http.StatusForbidden, ErrorResponse{
				Message: message,
				Success: false,
			})
			return
		}

		next(w, req)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/grafana/grafana/pkg/log"
	"github.com/raintank/raintank-metric/dur"
	"github.com/raintank/raintank-metric/metric_tank/consolidation"
	"github.com/raintank/raintank-metric/schema"
	"math"
	"net/http"
	_ "net/http/pprof"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var errMetricNotFound = errors.New("metric not found")

var bufPool = sync.Pool{
	New: func() interface{} { return make([]byte, 0) },
}

type Series struct {
	Target     string
	Datapoints []schema.Point
	Interval   uint32
}

func listJSON(b []byte, defs []*schema.MetricDefinition) ([]byte, error) {
	names := make([]string, len(defs))
	for i := 0; i < len(defs); i++ {
		names[i] = defs[i].Name
	}
	sort.Strings(names)
	b = append(b, '[')
	for _, name := range names {
		b = append(b, '"')
		b = append(b, name...)
		b = append(b, `",`...)
	}
	if len(defs) != 0 {
		b = b[:len(b)-1] // cut last comma
	}
	b = append(b, ']')
	return b, nil
}

// regular graphite output
func graphiteJSON(b []byte, series []Series) ([]byte, error) {
	b = append(b, '[')
	for _, s := range series {
		b = append(b, `{"target":"`...)
		b = append(b, s.Target...)
		b = append(b, `","datapoints":[`...)
		for _, p := range s.Datapoints {
			b = append(b, '[')
			if math.IsNaN(p.Val) {
				b = append(b, `null,`...)
			} else {
				b = strconv.AppendFloat(b, p.Val, 'f', 3, 64)
				b = append(b, ',')
			}
			b = strconv.AppendUint(b, uint64(p.Ts), 10)
			b = append(b, `],`...)
		}
		if len(s.Datapoints) != 0 {
			b = b[:len(b)-1] // cut last comma
		}
		b = append(b, `]},`...)
	}
	if len(series) != 0 {
		b = b[:len(b)-1] // cut last comma
	}
	b = append(b, ']')
	return b, nil
}

// data output for graphite raintank target -> Target, datapoints -> Datapoints, and adds Interval field
func graphiteRaintankJSON(b []byte, series []Series) ([]byte, error) {
	b = append(b, '[')
	for _, s := range series {
		b = append(b, `{"Target":"`...)
		b = append(b, s.Target...)
		b = append(b, `","Datapoints":[`...)
		for _, p := range s.Datapoints {
			b = append(b, '[')
			if math.IsNaN(p.Val) {
				b = append(b, `null,`...)
			} else {
				b = strconv.AppendFloat(b, p.Val, 'f', 3, 64)
				b = append(b, ',')
			}
			b = strconv.AppendUint(b, uint64(p.Ts), 10)
			b = append(b, `],`...)
		}
		if len(s.Datapoints) != 0 {
			b = b[:len(b)-1] // cut last comma
		}
		b = append(b, `],"Interval":`...)
		b = strconv.AppendInt(b, int64(s.Interval), 10)
		b = append(b, `},`...)
	}
	if len(series) != 0 {
		b = b[:len(b)-1] // cut last comma
	}
	b = append(b, ']')
	return b, nil
}

func IndexJson(defCache *DefCache) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		list := defCache.List()
		js := bufPool.Get().([]byte)
		js, err := listJSON(js, list)
		if err != nil {
			log.Error(0, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
		bufPool.Put(js[:0])
	}
}
func get(store Store, defCache *DefCache, aggSettings []aggSetting, logMinDur uint32) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		Get(w, req, store, defCache, aggSettings, logMinDur, false)
	}
}

func getLegacy(store Store, defCache *DefCache, aggSettings []aggSetting, logMinDur uint32) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		Get(w, req, store, defCache, aggSettings, logMinDur, true)
	}
}

// note: we don't normalize/quantize/fill-unknowns
// we just serve what we know
func Get(w http.ResponseWriter, req *http.Request, store Store, defCache *DefCache, aggSettings []aggSetting, logMinDur uint32, legacy bool) {
	pre := time.Now()
	req.ParseForm()

	maxDataPoints := uint32(800)
	maxDataPointsStr := req.Form.Get("maxDataPoints")
	var err error
	if maxDataPointsStr != "" {
		tmp, err := strconv.Atoi(maxDataPointsStr)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		maxDataPoints = uint32(tmp)
	}

	targets, ok := req.Form["target"]
	if !ok {
		http.Error(w, "missing target arg", http.StatusBadRequest)
		return
	}
	if len(targets)*int(maxDataPoints) > 500*2000 {
		http.Error(w, "too many targets/maxDataPoints requested", http.StatusBadRequest)
		return
	}

	now := time.Now()
	fromUnix := uint32(now.Add(-time.Duration(24) * time.Hour).Unix())
	toUnix := uint32(now.Add(time.Duration(1) * time.Second).Unix())
	from := req.Form.Get("from")
	if from != "" {
		fromUnixInt, err := strconv.Atoi(from)
		if err == nil {
			fromUnix = uint32(fromUnixInt)
		} else {
			if len(from) == 1 || from[0] != '-' {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			dur, err := dur.ParseUNsec(from[1:])
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			fromUnix = uint32(now.Add(-time.Duration(dur) * time.Second).Unix())
		}
	}
	to := req.Form.Get("to")
	if to != "" {
		toUnixInt, err := strconv.Atoi(to)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		toUnix = uint32(toUnixInt)
	}
	if fromUnix >= toUnix {
		http.Error(w, "to must be higher than from", http.StatusBadRequest)
		return
	}
	if len(targets)*int(toUnix-fromUnix) > 500*2*365*24*3600 {
		http.Error(w, "too many targets/too large timeframe requested", http.StatusBadRequest)
		return
	}

	reqs := make([]Req, len(targets))
	for i, target := range targets {
		var consolidateBy string
		id := target
		// yes, i am aware of the arguably grossness of the below.
		// however, it is solid based on the documented allowed input format.
		// once we need to support several functions, we can implement
		// a proper expression parser
		if strings.HasPrefix(target, "consolidateBy(") {
			if target[len(target)-2:] != "')" || !strings.Contains(target, ",'") || strings.Count(target, "'") != 2 || strings.Count(target, ",") != 1 {
				http.Error(w, "target parse error", http.StatusBadRequest)
				return
			}
			consolidateBy = target[strings.Index(target, "'")+1 : strings.LastIndex(target, "'")]
			id = target[strings.Index(target, "(")+1 : strings.Index(target, ",")]
		}

		// if we're serving the legacy graphite api, set the id field by looking up the graphite target
		if legacy {
			def, ok := defCache.GetByKey(id)
			if !ok {
				http.Error(w, errMetricNotFound.Error(), http.StatusBadRequest)
				return
			}
			id = def.Id
		}

		if consolidateBy == "" {
			def, ok := defCache.Get(id)
			consolidateBy = "avg"
			if ok && def.TargetType == "counter" {
				consolidateBy = "last"
			}
		}
		var consolidator consolidation.Consolidator
		switch consolidateBy {
		case "avg", "average":
			consolidator = consolidation.Avg
		case "min":
			consolidator = consolidation.Min
		case "max":
			consolidator = consolidation.Max
		case "sum":
			consolidator = consolidation.Sum
		default:
			http.Error(w, "unrecognized consolidation function", http.StatusBadRequest)
			return
		}
		req := NewReq(id, target, fromUnix, toUnix, maxDataPoints, consolidator)
		reqs[i] = req
	}
	if (toUnix - fromUnix) >= logMinDur {
		log.Info("http.Get(): INCOMING REQ %q from: %q, to: %q targets: %q, maxDataPoints: %q",
			req.Method, req.Form.Get("from"), req.Form.Get("to"), req.Form["target"], req.Form.Get("maxDataPoints"))
	}

	if logLevel < 2 {
		for _, req := range reqs {
			log.Debug("HTTP Get() %s", req)
		}
	}

	err = findMetricsForRequests(reqs, defCache)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	reqs, err = alignRequests(reqs, aggSettings)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	out, err := getTargets(store, reqs)
	if err != nil {
		log.Error(0, err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	js := bufPool.Get().([]byte)
	if legacy {
		js, err = graphiteJSON(js, out)
	} else {
		js, err = graphiteRaintankJSON(js, out)
	}
	for _, serie := range out {
		pointSlicePool.Put(serie.Datapoints[:0])
	}
	if err != nil {
		bufPool.Put(js[:0])
		log.Error(0, err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	reqHandleDuration.Value(time.Now().Sub(pre))
	w.Write(js)
	bufPool.Put(js[:0])
}

// report ApplicationStatus for use by loadBalancer healthChecks.
// We only want requests to be sent to this node if it is the primary
// node or if it has been online for at *warmUpPeriod
func appStatus(w http.ResponseWriter, req *http.Request) {
	if clusterStatus.IsPrimary() {
		w.Write([]byte("OK"))
		return
	}
	if time.Since(startupTime) < warmupPeriod {
		http.Error(w, "Service not ready", http.StatusServiceUnavailable)
		return
	}

	w.Write([]byte("OK"))
	return
}
<commit_msg>log error when given id can't be loaded. fix #117<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/grafana/grafana/pkg/log"
	"github.com/raintank/raintank-metric/dur"
	"github.com/raintank/raintank-metric/metric_tank/consolidation"
	"github.com/raintank/raintank-metric/schema"
	"math"
	"net/http"
	_ "net/http/pprof"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var errMetricNotFound = errors.New("metric not found")

var bufPool = sync.Pool{
	New: func() interface{} { return make([]byte, 0) },
}

type Series struct {
	Target     string
	Datapoints []schema.Point
	Interval   uint32
}

func listJSON(b []byte, defs []*schema.MetricDefinition) ([]byte, error) {
	names := make([]string, len(defs))
	for i := 0; i < len(defs); i++ {
		names[i] = defs[i].Name
	}
	sort.Strings(names)
	b = append(b, '[')
	for _, name := range names {
		b = append(b, '"')
		b = append(b, name...)
		b = append(b, `",`...)
	}
	if len(defs) != 0 {
		b = b[:len(b)-1] // cut last comma
	}
	b = append(b, ']')
	return b, nil
}

// regular graphite output
func graphiteJSON(b []byte, series []Series) ([]byte, error) {
	b = append(b, '[')
	for _, s := range series {
		b = append(b, `{"target":"`...)
		b = append(b, s.Target...)
		b = append(b, `","datapoints":[`...)
		for _, p := range s.Datapoints {
			b = append(b, '[')
			if math.IsNaN(p.Val) {
				b = append(b, `null,`...)
			} else {
				b = strconv.AppendFloat(b, p.Val, 'f', 3, 64)
				b = append(b, ',')
			}
			b = strconv.AppendUint(b, uint64(p.Ts), 10)
			b = append(b, `],`...)
		}
		if len(s.Datapoints) != 0 {
			b = b[:len(b)-1] // cut last comma
		}
		b = append(b, `]},`...)
	}
	if len(series) != 0 {
		b = b[:len(b)-1] // cut last comma
	}
	b = append(b, ']')
	return b, nil
}

// data output for graphite raintank target -> Target, datapoints -> Datapoints, and adds Interval field
func graphiteRaintankJSON(b []byte, series []Series) ([]byte, error) {
	b = append(b, '[')
	for _, s := range series {
		b = append(b, `{"Target":"`...)
		b = append(b, s.Target...)
		b = append(b, `","Datapoints":[`...)
		for _, p := range s.Datapoints {
			b = append(b, '[')
			if math.IsNaN(p.Val) {
				b = append(b, `null,`...)
			} else {
				b = strconv.AppendFloat(b, p.Val, 'f', 3, 64)
				b = append(b, ',')
			}
			b = strconv.AppendUint(b, uint64(p.Ts), 10)
			b = append(b, `],`...)
		}
		if len(s.Datapoints) != 0 {
			b = b[:len(b)-1] // cut last comma
		}
		b = append(b, `],"Interval":`...)
		b = strconv.AppendInt(b, int64(s.Interval), 10)
		b = append(b, `},`...)
	}
	if len(series) != 0 {
		b = b[:len(b)-1] // cut last comma
	}
	b = append(b, ']')
	return b, nil
}

func IndexJson(defCache *DefCache) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		list := defCache.List()
		js := bufPool.Get().([]byte)
		js, err := listJSON(js, list)
		if err != nil {
			log.Error(0, err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
		bufPool.Put(js[:0])
	}
}
func get(store Store, defCache *DefCache, aggSettings []aggSetting, logMinDur uint32) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		Get(w, req, store, defCache, aggSettings, logMinDur, false)
	}
}

func getLegacy(store Store, defCache *DefCache, aggSettings []aggSetting, logMinDur uint32) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		Get(w, req, store, defCache, aggSettings, logMinDur, true)
	}
}

// note: we don't normalize/quantize/fill-unknowns
// we just serve what we know
func Get(w http.ResponseWriter, req *http.Request, store Store, defCache *DefCache, aggSettings []aggSetting, logMinDur uint32, legacy bool) {
	pre := time.Now()
	req.ParseForm()

	maxDataPoints := uint32(800)
	maxDataPointsStr := req.Form.Get("maxDataPoints")
	var err error
	if maxDataPointsStr != "" {
		tmp, err := strconv.Atoi(maxDataPointsStr)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		maxDataPoints = uint32(tmp)
	}

	targets, ok := req.Form["target"]
	if !ok {
		http.Error(w, "missing target arg", http.StatusBadRequest)
		return
	}
	if len(targets)*int(maxDataPoints) > 500*2000 {
		http.Error(w, "too many targets/maxDataPoints requested", http.StatusBadRequest)
		return
	}

	now := time.Now()
	fromUnix := uint32(now.Add(-time.Duration(24) * time.Hour).Unix())
	toUnix := uint32(now.Add(time.Duration(1) * time.Second).Unix())
	from := req.Form.Get("from")
	if from != "" {
		fromUnixInt, err := strconv.Atoi(from)
		if err == nil {
			fromUnix = uint32(fromUnixInt)
		} else {
			if len(from) == 1 || from[0] != '-' {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			dur, err := dur.ParseUNsec(from[1:])
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			fromUnix = uint32(now.Add(-time.Duration(dur) * time.Second).Unix())
		}
	}
	to := req.Form.Get("to")
	if to != "" {
		toUnixInt, err := strconv.Atoi(to)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		toUnix = uint32(toUnixInt)
	}
	if fromUnix >= toUnix {
		http.Error(w, "to must be higher than from", http.StatusBadRequest)
		return
	}
	if len(targets)*int(toUnix-fromUnix) > 500*2*365*24*3600 {
		http.Error(w, "too many targets/too large timeframe requested", http.StatusBadRequest)
		return
	}

	reqs := make([]Req, len(targets))
	for i, target := range targets {
		var consolidateBy string
		id := target
		// yes, i am aware of the arguably grossness of the below.
		// however, it is solid based on the documented allowed input format.
		// once we need to support several functions, we can implement
		// a proper expression parser
		if strings.HasPrefix(target, "consolidateBy(") {
			if target[len(target)-2:] != "')" || !strings.Contains(target, ",'") || strings.Count(target, "'") != 2 || strings.Count(target, ",") != 1 {
				http.Error(w, "target parse error", http.StatusBadRequest)
				return
			}
			consolidateBy = target[strings.Index(target, "'")+1 : strings.LastIndex(target, "'")]
			id = target[strings.Index(target, "(")+1 : strings.Index(target, ",")]
		}

		// if we're serving the legacy graphite api, set the id field by looking up the graphite target
		if legacy {
			def, ok := defCache.GetByKey(id)
			if !ok {
				http.Error(w, errMetricNotFound.Error(), http.StatusBadRequest)
				return
			}
			id = def.Id
		}

		if consolidateBy == "" {
			def, ok := defCache.Get(id)
			consolidateBy = "avg"
			if !ok {
				e := fmt.Sprintf("metric %q not found", id)
				log.Error(0, e)
				http.Error(w, e, http.StatusBadRequest)
				return
			}
			if def.TargetType == "counter" {
				consolidateBy = "last"
			}
		}
		var consolidator consolidation.Consolidator
		switch consolidateBy {
		case "avg", "average":
			consolidator = consolidation.Avg
		case "min":
			consolidator = consolidation.Min
		case "max":
			consolidator = consolidation.Max
		case "sum":
			consolidator = consolidation.Sum
		default:
			http.Error(w, "unrecognized consolidation function", http.StatusBadRequest)
			return
		}
		req := NewReq(id, target, fromUnix, toUnix, maxDataPoints, consolidator)
		reqs[i] = req
	}
	if (toUnix - fromUnix) >= logMinDur {
		log.Info("http.Get(): INCOMING REQ %q from: %q, to: %q targets: %q, maxDataPoints: %q",
			req.Method, req.Form.Get("from"), req.Form.Get("to"), req.Form["target"], req.Form.Get("maxDataPoints"))
	}

	if logLevel < 2 {
		for _, req := range reqs {
			log.Debug("HTTP Get() %s", req)
		}
	}

	err = findMetricsForRequests(reqs, defCache)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	reqs, err = alignRequests(reqs, aggSettings)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	out, err := getTargets(store, reqs)
	if err != nil {
		log.Error(0, err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	js := bufPool.Get().([]byte)
	if legacy {
		js, err = graphiteJSON(js, out)
	} else {
		js, err = graphiteRaintankJSON(js, out)
	}
	for _, serie := range out {
		pointSlicePool.Put(serie.Datapoints[:0])
	}
	if err != nil {
		bufPool.Put(js[:0])
		log.Error(0, err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	reqHandleDuration.Value(time.Now().Sub(pre))
	w.Write(js)
	bufPool.Put(js[:0])
}

// report ApplicationStatus for use by loadBalancer healthChecks.
// We only want requests to be sent to this node if it is the primary
// node or if it has been online for at *warmUpPeriod
func appStatus(w http.ResponseWriter, req *http.Request) {
	if clusterStatus.IsPrimary() {
		w.Write([]byte("OK"))
		return
	}
	if time.Since(startupTime) < warmupPeriod {
		http.Error(w, "Service not ready", http.StatusServiceUnavailable)
		return
	}

	w.Write([]byte("OK"))
	return
}
<|endoftext|>


<commit_before>// +build linux

/*
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 mount

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/nsenter"
)

const (
	// hostProcMountsPath is the default mount path for rootfs
	hostProcMountsPath = "/rootfs/proc/1/mounts"
	// hostProcMountinfoPath is the default mount info path for rootfs
	hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
)

// Currently, all docker containers receive their own mount namespaces.
// NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
type NsenterMounter struct {
	ne *nsenter.Nsenter
}

func NewNsenterMounter() *NsenterMounter {
	return &NsenterMounter{ne: nsenter.NewNsenter()}
}

// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})

// Mount runs mount(8) in the host's root mount namespace.  Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
	bind, bindRemountOpts := isBind(options)

	if bind {
		err := n.doNsenterMount(source, target, fstype, []string{"bind"})
		if err != nil {
			return err
		}
		return n.doNsenterMount(source, target, fstype, bindRemountOpts)
	}

	return n.doNsenterMount(source, target, fstype, options)
}

// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
	glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
	cmd, args := n.makeNsenterArgs(source, target, fstype, options)
	outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
	}
	return err
}

// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
	mountCmd := n.ne.AbsHostPath("mount")
	mountArgs := makeMountArgs(source, target, fstype, options)

	if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes
		//   host's systemd-run.
		// * systemd-run creates a transient scope (=~ cgroup) and executes its
		//   argument (/bin/mount) there.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		//   (systemd-run --scope finishes at this point, returning mount's exit
		//   code and stdout/stderr - thats one of --scope benefits).
		// * systemd keeps the fuse daemon running in the scope (i.e. in its own
		//   cgroup) until the fuse daemon dies (another --scope benefit).
		//   Kubelet container can be restarted and the fuse daemon survives.
		// * When the daemon dies (e.g. during unmount) systemd removes the
		//   scope automatically.
		mountCmd, mountArgs = addSystemdScope(systemdRunPath, target, mountCmd, mountArgs)
	} else {
		// Fall back to simple mount when the host has no systemd.
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes host's /bin/mount.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		// * Any fuse daemon runs in cgroup of kubelet docker container,
		//   restart of kubelet container will kill it!

		// No code here, mountCmd and mountArgs use /bin/mount
	}

	return mountCmd, mountArgs
}

// Unmount runs umount(8) in the host's mount namespace.
func (n *NsenterMounter) Unmount(target string) error {
	args := []string{target}
	// No need to execute systemd-run here, it's enough that unmount is executed
	// in the host's mount namespace. It will finish appropriate fuse daemon(s)
	// running in any scope.
	glog.V(5).Infof("nsenter unmount args: %v", args)
	outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
	}
	return err
}

// List returns a list of all mounted filesystems in the host's mount namespace.
func (*NsenterMounter) List() ([]MountPoint, error) {
	return listProcMounts(hostProcMountsPath)
}

func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
	return IsNotMountPoint(m, dir)
}

func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
	deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
	return (mp.Path == dir) || (mp.Path == deletedDir)
}

// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
// in the host's root mount namespace.
func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
	file, err := filepath.Abs(file)
	if err != nil {
		return true, err
	}

	// Check the directory exists
	if _, err = os.Stat(file); os.IsNotExist(err) {
		glog.V(5).Infof("findmnt: directory %s does not exist", file)
		return true, err
	}
	// Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get only
	// the first of multiple possible mountpoints using --first-only.
	// Also add fstype output to make sure that the output of target file will give the full path
	// TODO: Need more refactoring for this function. Track the solution with issue #26996
	args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
	glog.V(5).Infof("nsenter findmnt args: %v", args)
	out, err := n.ne.Exec("findmnt", args).CombinedOutput()
	if err != nil {
		glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
		// Different operating systems behave differently for paths which are not mount points.
		// On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
		// It's safer to assume that it's not a mount point.
		return true, nil
	}
	mountTarget, err := parseFindMnt(string(out))
	if err != nil {
		return false, err
	}

	glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)

	if mountTarget == file {
		glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
		return false, nil
	}
	glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
	return true, nil
}

// parse output of "findmnt -o target,fstype" and return just the target
func parseFindMnt(out string) (string, error) {
	// cut trailing newline
	out = strings.TrimSuffix(out, "\n")
	// cut everything after the last space - it's the filesystem type
	i := strings.LastIndex(out, " ")
	if i == -1 {
		return "", fmt.Errorf("error parsing findmnt output, expected at least one space: %q", out)
	}
	return out[:i], nil
}

// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device.
func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
	return exclusiveOpenFailsOnDevice(pathname)
}

// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
	pathType, err := n.GetFileType(pathname)
	isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
	return isDevice, err
}

//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return getDeviceNameFromMount(n, mountPath, pluginDir)
}

func (n *NsenterMounter) MakeRShared(path string) error {
	return doMakeRShared(path, hostProcMountinfoPath)
}

func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
	var pathType FileType
	outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
	if err != nil {
		return pathType, err
	}

	switch string(outputBytes) {
	case "socket":
		return FileTypeSocket, nil
	case "character special file":
		return FileTypeCharDev, nil
	case "block special file":
		return FileTypeBlockDev, nil
	case "directory":
		return FileTypeDirectory, nil
	case "regular file":
		return FileTypeFile, nil
	}

	return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

func (mounter *NsenterMounter) MakeDir(pathname string) error {
	args := []string{"-p", pathname}
	if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) MakeFile(pathname string) error {
	args := []string{pathname}
	if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
	args := []string{pathname}
	_, err := mounter.ne.Exec("ls", args).CombinedOutput()
	if err == nil {
		return true
	}
	return false
}
<commit_msg>Show findmt command output in case of error<commit_after>// +build linux

/*
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 mount

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/nsenter"
)

const (
	// hostProcMountsPath is the default mount path for rootfs
	hostProcMountsPath = "/rootfs/proc/1/mounts"
	// hostProcMountinfoPath is the default mount info path for rootfs
	hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
)

// Currently, all docker containers receive their own mount namespaces.
// NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
type NsenterMounter struct {
	ne *nsenter.Nsenter
}

func NewNsenterMounter() *NsenterMounter {
	return &NsenterMounter{ne: nsenter.NewNsenter()}
}

// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})

// Mount runs mount(8) in the host's root mount namespace.  Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
	bind, bindRemountOpts := isBind(options)

	if bind {
		err := n.doNsenterMount(source, target, fstype, []string{"bind"})
		if err != nil {
			return err
		}
		return n.doNsenterMount(source, target, fstype, bindRemountOpts)
	}

	return n.doNsenterMount(source, target, fstype, options)
}

// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
	glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
	cmd, args := n.makeNsenterArgs(source, target, fstype, options)
	outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
	}
	return err
}

// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
	mountCmd := n.ne.AbsHostPath("mount")
	mountArgs := makeMountArgs(source, target, fstype, options)

	if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes
		//   host's systemd-run.
		// * systemd-run creates a transient scope (=~ cgroup) and executes its
		//   argument (/bin/mount) there.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		//   (systemd-run --scope finishes at this point, returning mount's exit
		//   code and stdout/stderr - thats one of --scope benefits).
		// * systemd keeps the fuse daemon running in the scope (i.e. in its own
		//   cgroup) until the fuse daemon dies (another --scope benefit).
		//   Kubelet container can be restarted and the fuse daemon survives.
		// * When the daemon dies (e.g. during unmount) systemd removes the
		//   scope automatically.
		mountCmd, mountArgs = addSystemdScope(systemdRunPath, target, mountCmd, mountArgs)
	} else {
		// Fall back to simple mount when the host has no systemd.
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes host's /bin/mount.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		// * Any fuse daemon runs in cgroup of kubelet docker container,
		//   restart of kubelet container will kill it!

		// No code here, mountCmd and mountArgs use /bin/mount
	}

	return mountCmd, mountArgs
}

// Unmount runs umount(8) in the host's mount namespace.
func (n *NsenterMounter) Unmount(target string) error {
	args := []string{target}
	// No need to execute systemd-run here, it's enough that unmount is executed
	// in the host's mount namespace. It will finish appropriate fuse daemon(s)
	// running in any scope.
	glog.V(5).Infof("nsenter unmount args: %v", args)
	outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
	}
	return err
}

// List returns a list of all mounted filesystems in the host's mount namespace.
func (*NsenterMounter) List() ([]MountPoint, error) {
	return listProcMounts(hostProcMountsPath)
}

func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
	return IsNotMountPoint(m, dir)
}

func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
	deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
	return (mp.Path == dir) || (mp.Path == deletedDir)
}

// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
// in the host's root mount namespace.
func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
	file, err := filepath.Abs(file)
	if err != nil {
		return true, err
	}

	// Check the directory exists
	if _, err = os.Stat(file); os.IsNotExist(err) {
		glog.V(5).Infof("findmnt: directory %s does not exist", file)
		return true, err
	}
	// Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get only
	// the first of multiple possible mountpoints using --first-only.
	// Also add fstype output to make sure that the output of target file will give the full path
	// TODO: Need more refactoring for this function. Track the solution with issue #26996
	args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
	glog.V(5).Infof("nsenter findmnt args: %v", args)
	out, err := n.ne.Exec("findmnt", args).CombinedOutput()
	if err != nil {
		glog.V(2).Infof("Failed findmnt command for path %s: %s %v", file, out, err)
		// Different operating systems behave differently for paths which are not mount points.
		// On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
		// It's safer to assume that it's not a mount point.
		return true, nil
	}
	mountTarget, err := parseFindMnt(string(out))
	if err != nil {
		return false, err
	}

	glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)

	if mountTarget == file {
		glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
		return false, nil
	}
	glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
	return true, nil
}

// parse output of "findmnt -o target,fstype" and return just the target
func parseFindMnt(out string) (string, error) {
	// cut trailing newline
	out = strings.TrimSuffix(out, "\n")
	// cut everything after the last space - it's the filesystem type
	i := strings.LastIndex(out, " ")
	if i == -1 {
		return "", fmt.Errorf("error parsing findmnt output, expected at least one space: %q", out)
	}
	return out[:i], nil
}

// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device.
func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
	return exclusiveOpenFailsOnDevice(pathname)
}

// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
	pathType, err := n.GetFileType(pathname)
	isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
	return isDevice, err
}

//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return getDeviceNameFromMount(n, mountPath, pluginDir)
}

func (n *NsenterMounter) MakeRShared(path string) error {
	return doMakeRShared(path, hostProcMountinfoPath)
}

func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
	var pathType FileType
	outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
	if err != nil {
		return pathType, err
	}

	switch string(outputBytes) {
	case "socket":
		return FileTypeSocket, nil
	case "character special file":
		return FileTypeCharDev, nil
	case "block special file":
		return FileTypeBlockDev, nil
	case "directory":
		return FileTypeDirectory, nil
	case "regular file":
		return FileTypeFile, nil
	}

	return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

func (mounter *NsenterMounter) MakeDir(pathname string) error {
	args := []string{"-p", pathname}
	if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) MakeFile(pathname string) error {
	args := []string{pathname}
	if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
	args := []string{pathname}
	_, err := mounter.ne.Exec("ls", args).CombinedOutput()
	if err == nil {
		return true
	}
	return false
}
<|endoftext|>