<commit_before>package gochimp3

import (
	"errors"
	"fmt"
)

const (
	members_path       = "/lists/%s/members"
	single_member_path = members_path + "/%s"

	member_activity_path = single_member_path + "/activity"
	member_goals_path    = single_member_path + "/goals"

	member_notes_path       = single_member_path + "/notes"
	single_member_note_path = member_notes_path + "/%s"

	member_tags_path       = single_member_path + "/tags"
	single_member_tag_path = member_tags_path + "/%s"

	delete_permanent_path = single_member_path + "/acttions/delete-permanent"
)

type ListOfMembers struct {
	baseList

	ListID  string   `json:"list_id"`
	Members []Member `json:"members"`
}

type MemberResponse struct {
	EmailAddress    string                 `json:"email_address"`
	EmailType       string                 `json:"email_type,omitempty"`
	Status          string                 `json:"status"`
	StatusIfNew     string                 `json:"status_if_new,omitempty"`
	MergeFields     map[string]interface{} `json:"merge_fields,omitempty"`
	Interests       map[string]bool        `json:"interests,omitempty"`
	Language        string                 `json:"language"`
	VIP             bool                   `json:"vip"`
	Location        *MemberLocation        `json:"location,omitempty"`
	IPOpt           string                 `json:"ip_opt,omitempty"`
	IPSignup        string                 `json:"ip_signup,omitempty"`
	Tags            []MemberTag            `json:"tags,omitempty"`
	TimestampSignup string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt    string                 `json:"timestamp_opt,omitempty"`
}

type MemberRequest struct {
	EmailAddress         string                 `json:"email_address"`
	EmailType            string                 `json:"email_type,omitempty"`
	Status               string                 `json:"status"`
	StatusIfNew          string                 `json:"status_if_new,omitempty"`
	MergeFields          map[string]interface{} `json:"merge_fields,omitempty"`
	Interests            map[string]bool        `json:"interests,omitempty"`
	Language             string                 `json:"language"`
	VIP                  bool                   `json:"vip"`
	Location             *MemberLocation        `json:"location,omitempty"`
	MarketingPermissions *MarketingPermissions  `json:"marketing_permissions,omitempty"`
	IPOpt                string                 `json:"ip_opt,omitempty"`
	IPSignup             string                 `json:"ip_signup,omitempty"`
	Tags                 []string               `json:"tags,omitempty"`
	TimestampSignup      string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt         string                 `json:"timestamp_opt,omitempty"`
}

type Member struct {
	MemberResponse

	ID            string          `json:"id"`
	ListID        string          `json:"list_id"`
	UniqueEmailID string          `json:"unique_email_id"`
	EmailType     string          `json:"email_type"`
	Stats         MemberStats     `json:"stats"`
	MemberRating  int             `json:"member_rating"`
	LastChanged   string          `json:"last_changed"`
	EmailClient   string          `json:"email_client"`
	LastNote      MemberNoteShort `json:"last_note"`

	api *API
}

func (mem *Member) CanMakeRequest() error {
	if mem.ListID == "" {
		return errors.New("No ListID provided")
	}

	if mem.ID == "" {
		return errors.New("No ID provided")
	}

	return nil
}

type MemberStats struct {
	AvgOpenRate  float64 `json:"avg_open_rate"`
	AvgClickRate float64 `json:"avg_click_rate"`
}

type MemberLocation struct {
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	GMTOffset   int     `json:"gmtoff"`
	DSTOffset   int     `json:"dstoff"`
	CountryCode string  `json:"country_code"`
	Timezone    string  `json:"timezone"`
}

type MarketingPermissions []MarketingPermission

type MarketingPermission struct {
	MarketingPermissionID string `json:"marketing_permission_id"`
	Text                  string `json:"text"`
	Enabled               bool   `json:"enabled"`
}

type MemberNoteShort struct {
	ID        int    `json:"note_id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	Note      string `json:"note"`
}

type MemberTag struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (list *ListResponse) GetMembers(params *InterestCategoriesQueryParams) (*ListOfMembers, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(ListOfMembers)

	err := list.api.Request("GET", endpoint, params, nil, response)
	if err != nil {
		return nil, err
	}

	for _, m := range response.Members {
		m.api = list.api
	}

	return response, nil
}

func (list *ListResponse) GetMember(id string, params *BasicQueryParams) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list *ListResponse) CreateMember(body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("POST", endpoint, nil, body, response)
}

func (list *ListResponse) UpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PATCH", endpoint, nil, body, response)
}

func (list *ListResponse) AddOrUpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PUT", endpoint, nil, body, response)
}

func (list *ListResponse) DeleteMember(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	return list.api.RequestOk("DELETE", endpoint)
}

func (list *ListResponse) DeleteMemberPermanent(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(delete_permanent_path, list.ID, id)
	return list.api.RequestOk("POST", endpoint)
}

// ------------------------------------------------------------------------------------------------
// Activity
// ------------------------------------------------------------------------------------------------

type ListOfMemberActivity struct {
	baseList

	EmailID  string     `json:"email_id"`
	ListID   string     `json:"list_id"`
	Activity []Activity `json:"activity"`
}

type MemberActivity struct {
	Action         string `json:"action"`
	Timestamp      string `json:"timestamp"`
	URL            string `json:"url"`
	Type           string `json:"type"`
	CampaignID     string `json:"campaign_id"`
	Title          string `json:"title"`
	ParentCampaign string `json:"parent_campaign"`
}

func (mem *Member) GetActivity(params *BasicQueryParams) (*ListOfMemberActivity, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_activity_path, mem.ListID, mem.ID)
	response := new(ListOfMemberActivity)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// Goals
// ------------------------------------------------------------------------------------------------

type ListOfMemberGoals struct {
	baseList

	EmailID string       `json:"email_id"`
	ListID  string       `json:"list_id"`
	Goals   []MemberGoal `json:"goals"`
}

type MemberGoal struct {
	ID            int    `json:"goal_id"`
	Event         string `json:"event"`
	LastVisitedAt string `json:"last_visited_at"`
	Data          string `json:"data"`
}

func (mem *Member) GetGoals(params *BasicQueryParams) (*ListOfMemberGoals, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_goals_path, mem.ListID, mem.ID)
	response := new(ListOfMemberGoals)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// NOTES
// ------------------------------------------------------------------------------------------------

type ListOfMemberNotes struct {
	baseList

	EmailID string           `json:"email_id"`
	ListID  string           `json:"list_id"`
	Notes   []MemberNoteLong `json:"notes"`
}

type MemberNoteLong struct {
	ID        int    `json:"id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	UpdatedAt string `json:"updated_at"`
	Note      string `json:"note"`
	ListID    string `json:"list_id"`
	EmailID   string `json:"email_id"`

	withLinks
}

func (mem *Member) GetNotes(params *ExtendedQueryParams) (*ListOfMemberNotes, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(ListOfMemberNotes)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) CreateNote(msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}

func (mem *Member) UpdateNote(id, msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("PATCH", endpoint, nil, &body, response)
}

func (mem *Member) GetNote(id string, params *BasicQueryParams) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) DeleteNote(id string) (bool, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	return mem.api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// TAGS
// ------------------------------------------------------------------------------------------------

type ListOfMemberTags struct {
	baseList

	Tags []MemberTagLong `json:"tags"`
}

type UpdateMemberTag struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

type MemberTagLong struct {
	ID        int     `json:"id"`
	Name      string  `json:"name"`
	DataAdded *string `json:"date_added,omitempty"`
	Status    string  `json:"status,omitempty"`

	withLinks
}

func (mem *Member) GetTags(params *ExtendedQueryParams) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) UpdateTags(tags []UpdateMemberTag) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	body := struct {
		Tags []UpdateMemberTag `json:"tags,omitempty"`
	}{
		Tags: tags,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}
<commit_msg>Fix delete permanently path<commit_after>package gochimp3

import (
	"errors"
	"fmt"
)

const (
	members_path       = "/lists/%s/members"
	single_member_path = members_path + "/%s"

	member_activity_path = single_member_path + "/activity"
	member_goals_path    = single_member_path + "/goals"

	member_notes_path       = single_member_path + "/notes"
	single_member_note_path = member_notes_path + "/%s"

	member_tags_path       = single_member_path + "/tags"
	single_member_tag_path = member_tags_path + "/%s"

	delete_permanent_path = single_member_path + "/actions/delete-permanent"
)

type ListOfMembers struct {
	baseList

	ListID  string   `json:"list_id"`
	Members []Member `json:"members"`
}

type MemberResponse struct {
	EmailAddress    string                 `json:"email_address"`
	EmailType       string                 `json:"email_type,omitempty"`
	Status          string                 `json:"status"`
	StatusIfNew     string                 `json:"status_if_new,omitempty"`
	MergeFields     map[string]interface{} `json:"merge_fields,omitempty"`
	Interests       map[string]bool        `json:"interests,omitempty"`
	Language        string                 `json:"language"`
	VIP             bool                   `json:"vip"`
	Location        *MemberLocation        `json:"location,omitempty"`
	IPOpt           string                 `json:"ip_opt,omitempty"`
	IPSignup        string                 `json:"ip_signup,omitempty"`
	Tags            []MemberTag            `json:"tags,omitempty"`
	TimestampSignup string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt    string                 `json:"timestamp_opt,omitempty"`
}

type MemberRequest struct {
	EmailAddress         string                 `json:"email_address"`
	EmailType            string                 `json:"email_type,omitempty"`
	Status               string                 `json:"status"`
	StatusIfNew          string                 `json:"status_if_new,omitempty"`
	MergeFields          map[string]interface{} `json:"merge_fields,omitempty"`
	Interests            map[string]bool        `json:"interests,omitempty"`
	Language             string                 `json:"language"`
	VIP                  bool                   `json:"vip"`
	Location             *MemberLocation        `json:"location,omitempty"`
	MarketingPermissions *MarketingPermissions  `json:"marketing_permissions,omitempty"`
	IPOpt                string                 `json:"ip_opt,omitempty"`
	IPSignup             string                 `json:"ip_signup,omitempty"`
	Tags                 []string               `json:"tags,omitempty"`
	TimestampSignup      string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt         string                 `json:"timestamp_opt,omitempty"`
}

type Member struct {
	MemberResponse

	ID            string          `json:"id"`
	ListID        string          `json:"list_id"`
	UniqueEmailID string          `json:"unique_email_id"`
	EmailType     string          `json:"email_type"`
	Stats         MemberStats     `json:"stats"`
	MemberRating  int             `json:"member_rating"`
	LastChanged   string          `json:"last_changed"`
	EmailClient   string          `json:"email_client"`
	LastNote      MemberNoteShort `json:"last_note"`

	api *API
}

func (mem *Member) CanMakeRequest() error {
	if mem.ListID == "" {
		return errors.New("No ListID provided")
	}

	if mem.ID == "" {
		return errors.New("No ID provided")
	}

	return nil
}

type MemberStats struct {
	AvgOpenRate  float64 `json:"avg_open_rate"`
	AvgClickRate float64 `json:"avg_click_rate"`
}

type MemberLocation struct {
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	GMTOffset   int     `json:"gmtoff"`
	DSTOffset   int     `json:"dstoff"`
	CountryCode string  `json:"country_code"`
	Timezone    string  `json:"timezone"`
}

type MarketingPermissions []MarketingPermission

type MarketingPermission struct {
	MarketingPermissionID string `json:"marketing_permission_id"`
	Text                  string `json:"text"`
	Enabled               bool   `json:"enabled"`
}

type MemberNoteShort struct {
	ID        int    `json:"note_id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	Note      string `json:"note"`
}

type MemberTag struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (list *ListResponse) GetMembers(params *InterestCategoriesQueryParams) (*ListOfMembers, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(ListOfMembers)

	err := list.api.Request("GET", endpoint, params, nil, response)
	if err != nil {
		return nil, err
	}

	for _, m := range response.Members {
		m.api = list.api
	}

	return response, nil
}

func (list *ListResponse) GetMember(id string, params *BasicQueryParams) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list *ListResponse) CreateMember(body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("POST", endpoint, nil, body, response)
}

func (list *ListResponse) UpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PATCH", endpoint, nil, body, response)
}

func (list *ListResponse) AddOrUpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PUT", endpoint, nil, body, response)
}

func (list *ListResponse) DeleteMember(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	return list.api.RequestOk("DELETE", endpoint)
}

func (list *ListResponse) DeleteMemberPermanent(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(delete_permanent_path, list.ID, id)
	return list.api.RequestOk("POST", endpoint)
}

// ------------------------------------------------------------------------------------------------
// Activity
// ------------------------------------------------------------------------------------------------

type ListOfMemberActivity struct {
	baseList

	EmailID  string     `json:"email_id"`
	ListID   string     `json:"list_id"`
	Activity []Activity `json:"activity"`
}

type MemberActivity struct {
	Action         string `json:"action"`
	Timestamp      string `json:"timestamp"`
	URL            string `json:"url"`
	Type           string `json:"type"`
	CampaignID     string `json:"campaign_id"`
	Title          string `json:"title"`
	ParentCampaign string `json:"parent_campaign"`
}

func (mem *Member) GetActivity(params *BasicQueryParams) (*ListOfMemberActivity, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_activity_path, mem.ListID, mem.ID)
	response := new(ListOfMemberActivity)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// Goals
// ------------------------------------------------------------------------------------------------

type ListOfMemberGoals struct {
	baseList

	EmailID string       `json:"email_id"`
	ListID  string       `json:"list_id"`
	Goals   []MemberGoal `json:"goals"`
}

type MemberGoal struct {
	ID            int    `json:"goal_id"`
	Event         string `json:"event"`
	LastVisitedAt string `json:"last_visited_at"`
	Data          string `json:"data"`
}

func (mem *Member) GetGoals(params *BasicQueryParams) (*ListOfMemberGoals, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_goals_path, mem.ListID, mem.ID)
	response := new(ListOfMemberGoals)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// NOTES
// ------------------------------------------------------------------------------------------------

type ListOfMemberNotes struct {
	baseList

	EmailID string           `json:"email_id"`
	ListID  string           `json:"list_id"`
	Notes   []MemberNoteLong `json:"notes"`
}

type MemberNoteLong struct {
	ID        int    `json:"id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	UpdatedAt string `json:"updated_at"`
	Note      string `json:"note"`
	ListID    string `json:"list_id"`
	EmailID   string `json:"email_id"`

	withLinks
}

func (mem *Member) GetNotes(params *ExtendedQueryParams) (*ListOfMemberNotes, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(ListOfMemberNotes)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) CreateNote(msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}

func (mem *Member) UpdateNote(id, msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("PATCH", endpoint, nil, &body, response)
}

func (mem *Member) GetNote(id string, params *BasicQueryParams) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) DeleteNote(id string) (bool, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	return mem.api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// TAGS
// ------------------------------------------------------------------------------------------------

type ListOfMemberTags struct {
	baseList

	Tags []MemberTagLong `json:"tags"`
}

type UpdateMemberTag struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

type MemberTagLong struct {
	ID        int     `json:"id"`
	Name      string  `json:"name"`
	DataAdded *string `json:"date_added,omitempty"`
	Status    string  `json:"status,omitempty"`

	withLinks
}

func (mem *Member) GetTags(params *ExtendedQueryParams) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) UpdateTags(tags []UpdateMemberTag) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	body := struct {
		Tags []UpdateMemberTag `json:"tags,omitempty"`
	}{
		Tags: tags,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}
<|endoftext|>


<commit_before>package mailwatcher

import (
	"bytes"
	"net/mail"
	"time"

	"code.google.com/p/go-imap/go1/imap"
)

// Response with header, uid and body of the mail
type Message struct {
	Header mail.Header
	Body   []byte
	UID    uint32
}

type Mail struct {
	User    string
	Pass    string
	Address string
	Num     uint32
	lastUID uint32
	client  *imap.Client
	cmd     *imap.Command
}

// Initialize new Mail
func New(user, pwd, addr string) (*Mail, error) {
	m := &Mail{
		User:    user,
		Pass:    pwd,
		Address: addr,
		Num:     1,
	}
	return m.Start()
}

// Runs dial and login
func (m *Mail) Start() (*Mail, error) {
	_, err := m.dial()
	if err != nil {
		return nil, err
	}
	_, err = m.login()
	if err != nil {
		return nil, err
	}
	return m, err
}

func (m *Mail) dial() (c *imap.Client, err error) {
	c, err = imap.DialTLS(m.Address, nil)
	m.client = c
	return
}

func (g *Mail) login() (*imap.Command, error) {
	g.client.StartTLS(nil)
	return g.client.Login(g.User, g.Pass)
}

// Fetch the latest email by passing as argument the mailbox and a filter (e.g. "UNSEEN") to be searched
func (m *Mail) Fetch(mailbox, filter string) (err error) {
	if mailbox == "" {
		mailbox = "INBOX"
	}

	if m.cmd, err = imap.Wait(m.client.Select(mailbox, false)); err != nil {
		return
	}

	if filter != "" {
		if m.cmd, err = imap.Wait(m.client.UIDSearch(filter)); err != nil {
			return
		}
	}

	set, _ := imap.NewSeqSet("")
	set.AddNum(m.client.Mailbox.Messages - m.Num)
	if m.cmd, err = imap.Wait(m.client.Fetch(set, "RFC822.HEADER", "UID", "BODY[1]")); err != nil {
		return
	}
	return
}

func (m *Mail) parseResponse(rsp *imap.Response) (uint32, []byte, *mail.Message, error) {
	info := rsp.MessageInfo()
	body := imap.AsBytes(info.Attrs["BODY[1]"])
	header := bytes.NewReader(imap.AsBytes(info.Attrs["RFC822.HEADER"]))
	hdr, err := mail.ReadMessage(header)
	if err != nil {
		return 0, nil, nil, err
	}
	return info.UID, body, hdr, nil
}

func (m *Mail) Messages() (msg []Message) {
	for _, rsp := range m.cmd.Data {
		uid, body, header, err := m.parseResponse(rsp)
		if err != nil {
			continue
		}
		msg = append(msg, Message{
			Header: header.Header,
			Body:   body,
			UID:    uid,
		})
	}
	return
}

// Blocks by looping with an specific interval and passes new incoming emails (inbox/unseen)
func (m *Mail) Watch(incoming chan Message, timer time.Duration) {
	ticker := time.NewTicker(time.Second * timer)
	for _ = range ticker.C {
		m.Fetch("INBOX", "UNSEEN")
		msgs := m.Messages()
		if len(msgs) == 0 {
			continue
		}
		if msg := msgs[0]; m.lastUID != uint32(0) && m.lastUID != msg.UID {
			m.lastUID = msg.UID
			incoming <- msg
		}
	}
}

// Same as Watch func, but using a callback instead of passing to a channel
func (m *Mail) WatchFunc(timer time.Duration, fn func(Message)) {
	incoming := make(chan Message, 1)
	go m.Watch(incoming, timer)
	for {
		msg := <-incoming
		fn(msg)
	}
}
<commit_msg>fixing Watch func; hiding Num field (as `num`); adding Mailbox field for the Select command<commit_after>package mailwatcher

import (
	"bytes"
	"net/mail"
	"time"

	"code.google.com/p/go-imap/go1/imap"
)

// Response with header, uid and body of the mail
type Message struct {
	Header mail.Header
	Body   []byte
	UID    uint32
}

type Mail struct {
	User    string
	Pass    string
	Address string
	Mailbox string
	num     uint32
	lastUID uint32
	client  *imap.Client
	cmd     *imap.Command
}

// Initialize new Mail
func New(user, pwd, addr string) (*Mail, error) {
	m := &Mail{
		User:    user,
		Pass:    pwd,
		Address: addr,
		Mailbox: "INBOX",
		num:     1,
	}
	return m.Start()
}

// Runs dial and login
func (m *Mail) Start() (*Mail, error) {
	_, err := m.dial()
	if err != nil {
		return nil, err
	}
	_, err = m.login()
	if err != nil {
		return nil, err
	}
	return m, err
}

func (m *Mail) dial() (c *imap.Client, err error) {
	c, err = imap.DialTLS(m.Address, nil)
	m.client = c
	return
}

func (g *Mail) login() (*imap.Command, error) {
	g.client.StartTLS(nil)
	return g.client.Login(g.User, g.Pass)
}

// Fetch the latest email using the field Mailbox and an argument for a filter (e.g. "UNSEEN") to be searched
func (m *Mail) Fetch(filter string) (err error) {
	if m.cmd, err = imap.Wait(m.client.Select(m.Mailbox, false)); err != nil {
		return
	}

	if filter != "" {
		if m.cmd, err = imap.Wait(m.client.UIDSearch(filter)); err != nil {
			return
		}
	}

	set, _ := imap.NewSeqSet("")
	set.AddNum(m.client.Mailbox.Messages - m.num)
	if m.cmd, err = imap.Wait(m.client.Fetch(set, "RFC822.HEADER", "UID", "BODY[1]")); err != nil {
		return
	}
	return
}

func (m *Mail) parseResponse(rsp *imap.Response) (uint32, []byte, *mail.Message, error) {
	info := rsp.MessageInfo()
	body := imap.AsBytes(info.Attrs["BODY[1]"])
	header := bytes.NewReader(imap.AsBytes(info.Attrs["RFC822.HEADER"]))
	hdr, err := mail.ReadMessage(header)
	if err != nil {
		return 0, nil, nil, err
	}
	return info.UID, body, hdr, nil
}

func (m *Mail) Messages() (msg []Message) {
	for _, rsp := range m.cmd.Data {
		uid, body, header, err := m.parseResponse(rsp)
		if err != nil {
			continue
		}
		msg = append(msg, Message{
			Header: header.Header,
			Body:   body,
			UID:    uid,
		})
	}
	return
}

// Blocks by looping with an specific interval and passes new incoming emails (inbox/unseen)
func (m *Mail) Watch(incoming chan Message, timer time.Duration) {
	ticker := time.NewTicker(time.Second * timer)
	for _ = range ticker.C {
		m.Fetch("UNSEEN")
		msgs := m.Messages()
		if len(msgs) == 0 {
			continue
		}

		msg := msgs[0]
		if m.lastUID == uint32(0) {
			m.lastUID = msg.UID
			continue
		}

		if m.lastUID != msg.UID {
			m.lastUID = msg.UID
			incoming <- msg
		}
	}
}

// Same as Watch func, but using a callback instead of passing to a channel
func (m *Mail) WatchFunc(timer time.Duration, fn func(Message)) {
	incoming := make(chan Message, 1)
	go m.Watch(incoming, timer)
	for {
		msg := <-incoming
		fn(msg)
	}
}
<|endoftext|>


<commit_before>package speed

import (
	"fmt"
	"sync"
)

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType MetricType = iota
	Int32Type
	Uint32Type
	Int64Type
	Uint64Type
	FloatType
	DoubleType
	StringType
	AggregateType
	AggregateStaticType
	EventType
	HighresEventType
	UnknownType
)

//go:generate stringer -type=MetricType

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit SpaceUnit = iota
	KilobyteUnit
	MegabyteUnit
	GigabyteUnit
	TerabyteUnit
	PetabyteUnit
	ExabyteUnit
)

//go:generate stringer -type=SpaceUnit

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit TimeUnit = iota
	MicrosecondUnit
	MillisecondUnit
	SecondUnit
	MinuteUnit
	HourUnit
)

//go:generate stringer -type=TimeUnit

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

//go:generate stringer -type=CountUnit

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics MetricSemantics = iota
	CounterSemantics
	InstantSemantics
	DiscreteSemantics
)

//go:generate stringer -type=MetricSemantics

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	ID() uint32                 // gets the unique id generated for this metric
	Name() string               // gets the name for the metric
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// PCPMetricItemBitLength is the maximum bit size of a PCP Metric id
//
// see: https://github.com/performancecopilot/pcp/blob/master/src/include/pcp/impl.h#L102-L121
const PCPMetricItemBitLength = 10

// MetricDesc is a metric metadata wrapper
// each metric type can wrap its metadata by containing a MetricDesc type and only define its own
// specific properties assuming MetricDesc will handle the rest
//
// when writing, this type is supposed to map directly to the pmDesc struct as defined in PCP core
type MetricDesc struct {
	id                                uint32          // unique metric id
	name                              string          // the name
	indom                             InstanceDomain  // the instance domain
	t                                 MetricType      // the type of a metric
	sem                               MetricSemantics // the semantics
	u                                 MetricUnit      // the unit
	offset                            int             // memory storage offset for the metric description
	shortDescription, longDescription string
}

// NewMetricDesc creates a new Metric Description wrapper type
func NewMetricDesc(n string, i InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *MetricDesc {
	return &MetricDesc{
		getHash(n, PCPMetricItemBitLength), n, i, t, s, u, 0, short, long,
	}
}

func (md *MetricDesc) Offset() int { return md.offset }

func (md *MetricDesc) SetOffset(offset int) { md.offset = offset }

func (md *MetricDesc) String() string {
	return fmt.Sprintf("%s{%v, %v, %v, %v}", md.name, md.indom, md.t, md.sem, md.u)
}

// PCPMetric defines a PCP compatible metric type that can be constructed by specifying values
// for type, semantics and unit
type PCPMetric struct {
	sync.RWMutex
	val    interface{} // all bets are off, store whatever you want
	desc   *MetricDesc // the metadata associated with this metric
	offset int         // memory storage offset for the metric value
}

// NewPCPMetric creates a new instance of PCPMetric
func NewPCPMetric(val interface{}, name string, indom InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *PCPMetric {
	return &PCPMetric{
		val:  val,
		desc: NewMetricDesc(name, indom, t, s, u, short, long),
	}
}

// Val returns the current set value of PCPMetric
func (m *PCPMetric) Val() interface{} {
	m.RLock()
	defer m.RUnlock()
	return m.val
}

// Set sets the current value of PCPMetric
func (m *PCPMetric) Set(val interface{}) error {
	if val != m.val {
		m.Lock()
		defer m.Unlock()
		m.val = val
	}
	return nil
}

// ID returns the generated id for PCPMetric
func (m *PCPMetric) ID() uint32 { return m.desc.id }

// Name returns the generated id for PCPMetric
func (m *PCPMetric) Name() string { return m.desc.name }

// Semantics returns the current stored value for PCPMetric
func (m *PCPMetric) Semantics() MetricSemantics { return m.desc.sem }

// Unit returns the unit for PCPMetric
func (m *PCPMetric) Unit() MetricUnit { return m.desc.u }

// Type returns the type for PCPMetric
func (m *PCPMetric) Type() MetricType { return m.desc.t }

// Description returns the description for PCPMetric
func (m *PCPMetric) Description() string {
	sd := m.desc.shortDescription
	ld := m.desc.longDescription
	if len(ld) > 0 {
		return sd + "\n\n" + ld
	}
	return sd
}

func (m *PCPMetric) Offset() int { return m.offset }

func (m *PCPMetric) SetOffset(offset int) { m.offset = offset }

func (m *PCPMetric) String() string {
	return fmt.Sprintf("Val: %v\n%v", m.val, m.Description())
}

// TODO: implement PCPCounterMetric, PCPGaugeMetric ...
<commit_msg>metrics: map MetricType to exact values in pmapi<commit_after>package speed

import (
	"fmt"
	"sync"
)

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = -1
	Int32Type           MetricType = 0
	Uint32Type          MetricType = 1
	Int64Type           MetricType = 2
	Uint64Type          MetricType = 3
	FloatType           MetricType = 4
	DoubleType          MetricType = 5
	StringType          MetricType = 6
	AggregateType       MetricType = 7
	AggregateStaticType MetricType = 8
	EventType           MetricType = 9
	HighresEventType    MetricType = 10
	UnknownType         MetricType = 255
)

//go:generate stringer -type=MetricType

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit SpaceUnit = iota
	KilobyteUnit
	MegabyteUnit
	GigabyteUnit
	TerabyteUnit
	PetabyteUnit
	ExabyteUnit
)

//go:generate stringer -type=SpaceUnit

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit TimeUnit = iota
	MicrosecondUnit
	MillisecondUnit
	SecondUnit
	MinuteUnit
	HourUnit
)

//go:generate stringer -type=TimeUnit

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

//go:generate stringer -type=CountUnit

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics MetricSemantics = iota
	CounterSemantics
	InstantSemantics
	DiscreteSemantics
)

//go:generate stringer -type=MetricSemantics

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	ID() uint32                 // gets the unique id generated for this metric
	Name() string               // gets the name for the metric
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// PCPMetricItemBitLength is the maximum bit size of a PCP Metric id
//
// see: https://github.com/performancecopilot/pcp/blob/master/src/include/pcp/impl.h#L102-L121
const PCPMetricItemBitLength = 10

// MetricDesc is a metric metadata wrapper
// each metric type can wrap its metadata by containing a MetricDesc type and only define its own
// specific properties assuming MetricDesc will handle the rest
//
// when writing, this type is supposed to map directly to the pmDesc struct as defined in PCP core
type MetricDesc struct {
	id                                uint32          // unique metric id
	name                              string          // the name
	indom                             InstanceDomain  // the instance domain
	t                                 MetricType      // the type of a metric
	sem                               MetricSemantics // the semantics
	u                                 MetricUnit      // the unit
	offset                            int             // memory storage offset for the metric description
	shortDescription, longDescription string
}

// NewMetricDesc creates a new Metric Description wrapper type
func NewMetricDesc(n string, i InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *MetricDesc {
	return &MetricDesc{
		getHash(n, PCPMetricItemBitLength), n, i, t, s, u, 0, short, long,
	}
}

func (md *MetricDesc) Offset() int { return md.offset }

func (md *MetricDesc) SetOffset(offset int) { md.offset = offset }

func (md *MetricDesc) String() string {
	return fmt.Sprintf("%s{%v, %v, %v, %v}", md.name, md.indom, md.t, md.sem, md.u)
}

// PCPMetric defines a PCP compatible metric type that can be constructed by specifying values
// for type, semantics and unit
type PCPMetric struct {
	sync.RWMutex
	val    interface{} // all bets are off, store whatever you want
	desc   *MetricDesc // the metadata associated with this metric
	offset int         // memory storage offset for the metric value
}

// NewPCPMetric creates a new instance of PCPMetric
func NewPCPMetric(val interface{}, name string, indom InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *PCPMetric {
	return &PCPMetric{
		val:  val,
		desc: NewMetricDesc(name, indom, t, s, u, short, long),
	}
}

// Val returns the current set value of PCPMetric
func (m *PCPMetric) Val() interface{} {
	m.RLock()
	defer m.RUnlock()
	return m.val
}

// Set sets the current value of PCPMetric
func (m *PCPMetric) Set(val interface{}) error {
	if val != m.val {
		m.Lock()
		defer m.Unlock()
		m.val = val
	}
	return nil
}

// ID returns the generated id for PCPMetric
func (m *PCPMetric) ID() uint32 { return m.desc.id }

// Name returns the generated id for PCPMetric
func (m *PCPMetric) Name() string { return m.desc.name }

// Semantics returns the current stored value for PCPMetric
func (m *PCPMetric) Semantics() MetricSemantics { return m.desc.sem }

// Unit returns the unit for PCPMetric
func (m *PCPMetric) Unit() MetricUnit { return m.desc.u }

// Type returns the type for PCPMetric
func (m *PCPMetric) Type() MetricType { return m.desc.t }

// Description returns the description for PCPMetric
func (m *PCPMetric) Description() string {
	sd := m.desc.shortDescription
	ld := m.desc.longDescription
	if len(ld) > 0 {
		return sd + "\n\n" + ld
	}
	return sd
}

func (m *PCPMetric) Offset() int { return m.offset }

func (m *PCPMetric) SetOffset(offset int) { m.offset = offset }

func (m *PCPMetric) String() string {
	return fmt.Sprintf("Val: %v\n%v", m.val, m.Description())
}

// TODO: implement PCPCounterMetric, PCPGaugeMetric ...
<|endoftext|>


<commit_before>package classic

import (
	"reflect"
	"sync/atomic"
	"testing"
	"time"
)

func (sem semaphore) Flush() {
	close(sem)
	for range sem {
	}
}

func TestLockingSemaphore(t *testing.T) {
	data := []int32{1, 2, 3}

	sem := NewLocking(len(data))
	defer sem.(semaphore).Flush()

	var sum int32

	for _, i := range data {
		go func(delta int32) {
			atomic.AddInt32(&sum, delta)
			sem.P(1)
		}(i)
	}
	sem.V(len(data))

	if int(sum) != 6 {
		t.Errorf("sum equal to 6 is expected, but received %d instead", sum)
	}
}

func TestSyncingSemaphore(t *testing.T) {
	data := []int32{1, 2, 3}

	sem := NewSyncing(len(data))
	defer sem.(semaphore).Flush()

	var sum int32

	for _, i := range data {
		go func(delta int32) {
			atomic.AddInt32(&sum, delta)
			sem.Signal()
		}(i)
	}
	sem.Wait(len(data))

	if int(sum) != 6 {
		t.Errorf("sum equal to 6 is expected, but received %d instead", sum)
	}
}

func TestBinarySemaphore(t *testing.T) {
	sem := NewBinary()
	defer sem.(semaphore).Flush()

	expected, steps := []string{"first", "second", "third"}, make([]string, 0, 3)

	sem.Lock()
	go func() {
		defer sem.Unlock()
		sem.Lock()

		steps = append(steps, "second")

		go func() {
			defer sem.Unlock()
			sem.Lock()

			steps = append(steps, "third")
		}()
	}()
	steps = append(steps, "first")
	sem.Unlock()

	// just enough to yield the scheduler and let the goroutines work off
	time.Sleep(time.Millisecond)

	sem.Lock()
	if !reflect.DeepEqual(expected, steps) {
		t.Errorf("%+v is not equal to %+v", steps, expected)
	}
	sem.Unlock()
}
<commit_msg>fix issue #44<commit_after>package classic

import (
	"reflect"
	"sync"
	"sync/atomic"
	"testing"
)

func (sem semaphore) Flush() {
	close(sem)
	for range sem {
	}
}

func TestLockingSemaphore(t *testing.T) {
	data := []int32{1, 2, 3}

	sem := NewLocking(len(data))
	defer sem.(semaphore).Flush()

	var sum int32

	for _, i := range data {
		go func(delta int32) {
			atomic.AddInt32(&sum, delta)
			sem.P(1)
		}(i)
	}
	sem.V(len(data))

	if int(sum) != 6 {
		t.Errorf("sum equal to 6 is expected, but received %d instead", sum)
	}
}

func TestSyncingSemaphore(t *testing.T) {
	data := []int32{1, 2, 3}

	sem := NewSyncing(len(data))
	defer sem.(semaphore).Flush()

	var sum int32

	for _, i := range data {
		go func(delta int32) {
			atomic.AddInt32(&sum, delta)
			sem.Signal()
		}(i)
	}
	sem.Wait(len(data))

	if int(sum) != 6 {
		t.Errorf("sum equal to 6 is expected, but received %d instead", sum)
	}
}

func TestBinarySemaphore(t *testing.T) {
	sem := NewBinary()
	defer sem.(semaphore).Flush()

	var v int
	from, to := []int{1, 2, 3}, make([]int, 0, 3)
	expected := []int{1, 2, 3}

	wg := sync.WaitGroup{}
	for i := 0; i < cap(expected); i++ {
		wg.Add(1)
		go func() {
			sem.Lock()
			v, from = from[0], from[1:]
			to = append(to, v)
			sem.Unlock()
			wg.Done()
		}()
	}
	wg.Wait()

	if !reflect.DeepEqual(expected, to) {
		t.Errorf("%+v is not equal to %+v", to, expected)
	}
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/config"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/metrics"
)

var (
	stats *peekStats
)

type peekStats struct {
	MsgPerSecond metrics.Meter
}

func newPeekStats() *peekStats {
	this := &peekStats{
		MsgPerSecond: metrics.NewMeter(),
	}

	metrics.Register("msg.per.second", this.MsgPerSecond)
	return this
}

func (this *peekStats) start() {
	metrics.Log(metrics.DefaultRegistry, time.Second*10,
		log.New(os.Stdout, "metrics: ", log.Lmicroseconds))
}

type Peek struct {
	Ui cli.Ui
}

func (this *Peek) Run(args []string) (exitCode int) {
	var (
		cluster     string
		zone        string
		topic       string
		partitionId int
	)
	cmdFlags := flag.NewFlagSet("peek", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", "", "")
	cmdFlags.StringVar(&cluster, "c", "", "")
	cmdFlags.StringVar(&topic, "t", "", "")
	cmdFlags.IntVar(&partitionId, "p", 0, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if zone == "" || cluster == "" {
		this.Ui.Output("-z zone and -c cluster required")
		this.Ui.Output(this.Help())
		return 2
	}

	stats := newPeekStats()
	go stats.start()

	zkzone := zk.NewZkZone(zk.DefaultConfig(zone, config.ZonePath(zone)))
	zkcluster := zkzone.NewCluster(cluster)
	brokerList := zkcluster.BrokerList()
	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		this.Ui.Output(err.Error())
		return 1
	}
	defer kfk.Close()

	msgChan := make(chan string, 1000)
	if topic == "" {
		// peek all topics
		topics, err := kfk.Topics()
		if err != nil {
			this.Ui.Output(err.Error())
			return 1
		}

		for _, t := range topics {
			go this.consumeTopic(kfk, t, int32(partitionId), msgChan)
		}
	} else {
		go this.consumeTopic(kfk, topic, int32(partitionId), msgChan)
	}

	var msg string
	for {
		select {
		case msg = <-msgChan:
			stats.MsgPerSecond.Mark(1)

			this.Ui.Output(msg)
		}
	}

	return
}

func (this *Peek) consumeTopic(kfk sarama.Client, topic string, partitionId int32, msgCh chan string) {
	consumer, err := sarama.NewConsumerFromClient(kfk)
	if err != nil {
		panic(err)
	}
	defer consumer.Close()

	p, _ := consumer.ConsumePartition(topic, partitionId, sarama.OffsetNewest)
	defer p.Close()

	for {
		select {
		case msg := <-p.Messages():
			msgCh <- fmt.Sprintf("%s %s %s", color.Green(topic),
				gofmt.Comma(msg.Offset), string(msg.Value))
		}
	}
}

func (*Peek) Synopsis() string {
	return "Peek kafka cluster messages ongoing"
}

func (*Peek) Help() string {
	help := `
Usage: gafka peek -z zone -c cluster [options]

	Peek kafka cluster messages ongoing

Options:

  -t topic name

  -p partition id
`
	return strings.TrimSpace(help)
}
<commit_msg>add a neat flag for peek command<commit_after>package command

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/config"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/metrics"
)

var (
	stats *peekStats
)

type peekStats struct {
	MsgPerSecond metrics.Meter
}

func newPeekStats() *peekStats {
	this := &peekStats{
		MsgPerSecond: metrics.NewMeter(),
	}

	metrics.Register("msg.per.second", this.MsgPerSecond)
	return this
}

func (this *peekStats) start() {
	metrics.Log(metrics.DefaultRegistry, time.Second*10,
		log.New(os.Stdout, "metrics: ", log.Lmicroseconds))
}

type Peek struct {
	Ui cli.Ui
}

func (this *Peek) Run(args []string) (exitCode int) {
	var (
		cluster     string
		zone        string
		topic       string
		partitionId int
		neat        bool
	)
	cmdFlags := flag.NewFlagSet("peek", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", "", "")
	cmdFlags.StringVar(&cluster, "c", "", "")
	cmdFlags.StringVar(&topic, "t", "", "")
	cmdFlags.IntVar(&partitionId, "p", 0, "")
	cmdFlags.BoolVar(&neat, "n", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if zone == "" || cluster == "" {
		this.Ui.Output("-z zone and -c cluster required")
		this.Ui.Output(this.Help())
		return 2
	}

	stats := newPeekStats()
	go stats.start()

	zkzone := zk.NewZkZone(zk.DefaultConfig(zone, config.ZonePath(zone)))
	zkcluster := zkzone.NewCluster(cluster)
	brokerList := zkcluster.BrokerList()
	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		this.Ui.Output(err.Error())
		return 1
	}
	defer kfk.Close()

	msgChan := make(chan string, 1000)
	if topic == "" {
		// peek all topics
		topics, err := kfk.Topics()
		if err != nil {
			this.Ui.Output(err.Error())
			return 1
		}

		for _, t := range topics {
			go this.consumeTopic(kfk, t, int32(partitionId), msgChan)
		}
	} else {
		go this.consumeTopic(kfk, topic, int32(partitionId), msgChan)
	}

	var msg string
	for {
		select {
		case msg = <-msgChan:
			stats.MsgPerSecond.Mark(1)

			if !neat {
				this.Ui.Output(msg)
			}
		}
	}

	return
}

func (this *Peek) consumeTopic(kfk sarama.Client, topic string, partitionId int32, msgCh chan string) {
	consumer, err := sarama.NewConsumerFromClient(kfk)
	if err != nil {
		panic(err)
	}
	defer consumer.Close()

	p, _ := consumer.ConsumePartition(topic, partitionId, sarama.OffsetNewest)
	defer p.Close()

	for {
		select {
		case msg := <-p.Messages():
			msgCh <- fmt.Sprintf("%s %s %s", color.Green(topic),
				gofmt.Comma(msg.Offset), string(msg.Value))
		}
	}
}

func (*Peek) Synopsis() string {
	return "Peek kafka cluster messages ongoing"
}

func (*Peek) Help() string {
	help := `
Usage: gafka peek -z zone -c cluster [options]

	Peek kafka cluster messages ongoing

Options:

  -t topic name

  -p partition id

  -n
  	Neat mode, only display statastics instead of message content
`
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/anycable/anycable-go/cli"
	"github.com/anycable/anycable-go/gobench"
	"github.com/anycable/anycable-go/metrics"
	"github.com/anycable/anycable-go/node"
	"github.com/anycable/anycable-go/server"
	"github.com/anycable/anycable-go/utils"

	log "github.com/apex/log"
	"github.com/syossan27/tebata"
)

func main() {
	if cli.ShowVersion() {
		fmt.Println(utils.Version())
		os.Exit(0)
	}

	if cli.ShowHelp() {
		cli.PrintHelp()
		os.Exit(0)
	}

	config := cli.Config()

	// init logging
	err := utils.InitLogger(config.LogFormat, config.LogLevel)

	if err != nil {
		log.Errorf("!!! Failed to initialize logger !!!\n%v", err)
		os.Exit(1)
	}

	ctx := log.WithFields(log.Fields{"context": "main"})

	if cli.DebugMode() {
		ctx.Debug("🔧 🔧 🔧 Debug mode is on 🔧 🔧 🔧")
	}

	ctx.Infof("Starting GoBenchCable %s (pid: %d, open file limit: %s)", utils.Version(), os.Getpid(), utils.OpenFileLimit())

	metrics, err := metrics.FromConfig(&config.Metrics)

	if err != nil {
		log.Errorf("!!! Failed to initialize custom log printer !!!\n%v", err)
		os.Exit(1)
	}

	controller := gobench.NewController(metrics)

	appNode := node.NewNode(controller, metrics)
	appNode.Start()

	// There could be different disconnectors in the future
	disconnector := node.NewDisconnectQueue(appNode, &config.DisconnectQueue)
	go disconnector.Run() // nolint:errcheck

	appNode.SetDisconnector(disconnector)

	go func() {
		if err := controller.Start(); err != nil {
			ctx.Errorf("!!! Controller failed !!!\n%v", err)
			os.Exit(1)
		}
	}()

	server.SSL = &config.SSL
	server.Host = config.Host

	wsServer, err := server.ForPort(strconv.Itoa(config.Port))
	if err != nil {
		fmt.Printf("!!! Failed to initialize WebSocket server at %s:%s !!!\n%v", err, config.Host, config.Port)
		os.Exit(1)
	}

	wsServer.Mux.Handle(config.Path, node.WebsocketHandler(appNode, config.Headers, &config.WS))
	ctx.Infof("Handle WebSocket connections at %s%s", wsServer.Address(), config.Path)

	wsServer.Mux.Handle(config.HealthPath, http.HandlerFunc(server.HealthHandler))
	ctx.Infof("Handle health connections at %s%s", wsServer.Address(), config.HealthPath)

	go func() {
		if err = wsServer.StartAndAnnounce("WebSocket server"); err != nil {
			if !wsServer.Stopped() {
				log.Errorf("WebSocket server at %s stopped: %v", err, wsServer.Address())
				os.Exit(1)
			}
		}
	}()

	go func() {
		if err := metrics.Run(); err != nil {
			ctx.Errorf("!!! Metrics module failed to start !!!\n%v", err)
			os.Exit(1)
		}
	}()

	t := tebata.New(syscall.SIGINT, syscall.SIGTERM)

	t.Reserve(func() { // nolint:errcheck
		ctx.Infof("Shutting down... (hit Ctrl-C to stop immediately)")
		go func() {
			termSig := make(chan os.Signal, 1)
			signal.Notify(termSig, syscall.SIGINT, syscall.SIGTERM)
			<-termSig
			ctx.Warnf("Immediate termination requested. Stopped")
			os.Exit(0)
		}()
	})
	t.Reserve(metrics.Shutdown) // nolint:errcheck
	t.Reserve(wsServer.Stop)    // nolint:errcheck
	t.Reserve(appNode.Shutdown) // nolint:errcheck

	t.Reserve(os.Exit, 0) // nolint:errcheck

	// Hang forever unless Exit is called
	select {}
}
<commit_msg>feat(gobench): support --disable_disconnect<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/anycable/anycable-go/cli"
	"github.com/anycable/anycable-go/gobench"
	"github.com/anycable/anycable-go/metrics"
	"github.com/anycable/anycable-go/node"
	"github.com/anycable/anycable-go/server"
	"github.com/anycable/anycable-go/utils"

	log "github.com/apex/log"
	"github.com/syossan27/tebata"
)

func main() {
	if cli.ShowVersion() {
		fmt.Println(utils.Version())
		os.Exit(0)
	}

	if cli.ShowHelp() {
		cli.PrintHelp()
		os.Exit(0)
	}

	config := cli.Config()

	// init logging
	err := utils.InitLogger(config.LogFormat, config.LogLevel)

	if err != nil {
		log.Errorf("!!! Failed to initialize logger !!!\n%v", err)
		os.Exit(1)
	}

	ctx := log.WithFields(log.Fields{"context": "main"})

	if cli.DebugMode() {
		ctx.Debug("🔧 🔧 🔧 Debug mode is on 🔧 🔧 🔧")
	}

	ctx.Infof("Starting GoBenchCable %s (pid: %d, open file limit: %s)", utils.Version(), os.Getpid(), utils.OpenFileLimit())

	metrics, err := metrics.FromConfig(&config.Metrics)

	if err != nil {
		log.Errorf("!!! Failed to initialize custom log printer !!!\n%v", err)
		os.Exit(1)
	}

	controller := gobench.NewController(metrics)

	appNode := node.NewNode(controller, metrics)
	appNode.Start()

	var disconnector node.Disconnector

	if config.DisconnectorDisabled {
		disconnector = node.NewNoopDisconnector()
	} else {
		disconnector = node.NewDisconnectQueue(appNode, &config.DisconnectQueue)
	}

	go disconnector.Run() // nolint:errcheck
	appNode.SetDisconnector(disconnector)

	go func() {
		if err := controller.Start(); err != nil {
			ctx.Errorf("!!! Controller failed !!!\n%v", err)
			os.Exit(1)
		}
	}()

	server.SSL = &config.SSL
	server.Host = config.Host

	wsServer, err := server.ForPort(strconv.Itoa(config.Port))
	if err != nil {
		fmt.Printf("!!! Failed to initialize WebSocket server at %s:%s !!!\n%v", err, config.Host, config.Port)
		os.Exit(1)
	}

	wsServer.Mux.Handle(config.Path, node.WebsocketHandler(appNode, config.Headers, &config.WS))
	ctx.Infof("Handle WebSocket connections at %s%s", wsServer.Address(), config.Path)

	wsServer.Mux.Handle(config.HealthPath, http.HandlerFunc(server.HealthHandler))
	ctx.Infof("Handle health connections at %s%s", wsServer.Address(), config.HealthPath)

	go func() {
		if err = wsServer.StartAndAnnounce("WebSocket server"); err != nil {
			if !wsServer.Stopped() {
				log.Errorf("WebSocket server at %s stopped: %v", err, wsServer.Address())
				os.Exit(1)
			}
		}
	}()

	go func() {
		if err := metrics.Run(); err != nil {
			ctx.Errorf("!!! Metrics module failed to start !!!\n%v", err)
			os.Exit(1)
		}
	}()

	t := tebata.New(syscall.SIGINT, syscall.SIGTERM)

	t.Reserve(func() { // nolint:errcheck
		ctx.Infof("Shutting down... (hit Ctrl-C to stop immediately)")
		go func() {
			termSig := make(chan os.Signal, 1)
			signal.Notify(termSig, syscall.SIGINT, syscall.SIGTERM)
			<-termSig
			ctx.Warnf("Immediate termination requested. Stopped")
			os.Exit(0)
		}()
	})
	t.Reserve(metrics.Shutdown) // nolint:errcheck
	t.Reserve(wsServer.Stop)    // nolint:errcheck
	t.Reserve(appNode.Shutdown) // nolint:errcheck

	t.Reserve(os.Exit, 0) // nolint:errcheck

	// Hang forever unless Exit is called
	select {}
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/juju/errors"
	"gopkg.in/juju/charm.v6-unstable"
	"gopkg.in/juju/charmrepo.v2-unstable"
	"gopkg.in/juju/charmrepo.v2-unstable/csclient"
	"gopkg.in/macaroon-bakery.v1/httpbakery"
	"gopkg.in/macaroon.v1"

	"github.com/juju/juju/api"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/environs/config"
)

// maybeTermsAgreementError returns err as a *termsAgreementError
// if it has a "terms agreement required" error code, otherwise
// it returns err unchanged.
func maybeTermsAgreementError(err error) error {
	const code = "term agreement required"
	e, ok := errors.Cause(err).(*httpbakery.DischargeError)
	if !ok || e.Reason == nil || e.Reason.Code != code {
		return err
	}
	magicMarker := code + ":"
	index := strings.LastIndex(e.Reason.Message, magicMarker)
	if index == -1 {
		return err
	}
	return &termsRequiredError{strings.Fields(e.Reason.Message[index+len(magicMarker):])}
}

type termsRequiredError struct {
	Terms []string
}

func (e *termsRequiredError) Error() string {
	return fmt.Sprintf("please agree to terms %q", strings.Join(e.Terms, " "))
}

func isSeriesSupported(requestedSeries string, supportedSeries []string) bool {
	for _, series := range supportedSeries {
		if series == requestedSeries {
			return true
		}
	}
	return false
}

type resolveCharmStoreEntityParams struct {
	urlStr          string
	requestedSeries string
	forceSeries     bool
	csParams        charmrepo.NewCharmStoreParams
	repoPath        string
	conf            *config.Config
}

// resolveCharmStoreEntityURL resolves the given charm or bundle URL string
// by looking it up in the appropriate charm repository.
// If it is a charm store URL, the given csParams will
// be used to access the charm store repository.
// If it is a local charm or bundle URL, the local charm repository at
// the given repoPath will be used. The given configuration
// will be used to add any necessary attributes to the repo
// and to return the charm's supported series if possible.
//
// resolveCharmStoreEntityURL also returns the charm repository holding
// the charm or bundle.
func resolveCharmStoreEntityURL(args resolveCharmStoreEntityParams) (*charm.URL, []string, charmrepo.Interface, error) {
	url, err := charm.ParseURL(args.urlStr)
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}
	repo, err := charmrepo.InferRepository(url, args.csParams, args.repoPath)
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}
	repo = config.SpecializeCharmRepo(repo, args.conf)

	if url.Schema == "local" && url.Series == "" {
		if defaultSeries, ok := args.conf.DefaultSeries(); ok {
			url.Series = defaultSeries
		}
		if url.Series == "" {
			possibleURL := *url
			possibleURL.Series = config.LatestLtsSeries()
			logger.Errorf("The series is not specified in the model (default-series) or with the charm. Did you mean:\n\t%s", &possibleURL)
			return nil, nil, nil, errors.Errorf("cannot resolve series for charm: %q", url)
		}
	}
	resultUrl, supportedSeries, err := repo.Resolve(url)
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}
	return resultUrl, supportedSeries, repo, nil
}

// addCharmFromURL calls the appropriate client API calls to add the
// given charm URL to state. For non-public charm URLs, this function also
// handles the macaroon authorization process using the given csClient.
// The resulting charm URL of the added charm is displayed on stdout.
func addCharmFromURL(client *api.Client, curl *charm.URL, repo charmrepo.Interface, csclient *csClient) (*charm.URL, error) {
	switch curl.Schema {
	case "local":
		ch, err := repo.Get(curl)
		if err != nil {
			return nil, err
		}
		stateCurl, err := client.AddLocalCharm(curl, ch)
		if err != nil {
			return nil, err
		}
		curl = stateCurl
	case "cs":
		if err := client.AddCharm(curl); err != nil {
			if !params.IsCodeUnauthorized(err) {
				return nil, errors.Trace(err)
			}
			m, err := csclient.authorize(curl)
			if err != nil {
				return nil, maybeTermsAgreementError(err)
			}
			if err := client.AddCharmWithAuthorization(curl, m); err != nil {
				return nil, errors.Trace(err)
			}
		}
	default:
		return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
	}
	return curl, nil
}

// csClient gives access to the charm store server and provides parameters
// for connecting to the charm store.
type csClient struct {
	params charmrepo.NewCharmStoreParams
}

// newCharmStoreClient is called to obtain a charm store client
// including the parameters for connecting to the charm store, and
// helpers to save the local authorization cookies and to authorize
// non-public charm deployments. It is defined as a variable so it can
// be changed for testing purposes.
var newCharmStoreClient = func(client *http.Client) *csClient {
	return &csClient{
		params: charmrepo.NewCharmStoreParams{
			HTTPClient:   client,
			VisitWebPage: httpbakery.OpenWebBrowser,
		},
	}
}

// authorize acquires and return the charm store delegatable macaroon to be
// used to add the charm corresponding to the given URL.
// The macaroon is properly attenuated so that it can only be used to deploy
// the given charm URL.
func (c *csClient) authorize(curl *charm.URL) (*macaroon.Macaroon, error) {
	if curl == nil {
		return nil, errors.New("empty charm url not allowed")
	}

	client := csclient.New(csclient.Params{
		URL:          c.params.URL,
		HTTPClient:   c.params.HTTPClient,
		VisitWebPage: c.params.VisitWebPage,
	})
	endpoint := "/delegatable-macaroon"
	endpoint += "?id=" + url.QueryEscape(curl.String())

	var m *macaroon.Macaroon
	if err := client.Get(endpoint, &m); err != nil {
		return nil, errors.Trace(err)
	}

	// We need to add the is-entity first party caveat to the
	// delegatable macaroon in case we're talking to the old
	// version of the charmstore.
	// TODO (ashipika) - remove this once the new charmstore
	// is deployed.
	if err := m.AddFirstPartyCaveat("is-entity " + curl.String()); err != nil {
		return nil, errors.Trace(err)
	}

	return m, nil
}
<commit_msg>Removed adding the is-entity caveat to the delagatable macaroon.<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/juju/errors"
	"gopkg.in/juju/charm.v6-unstable"
	"gopkg.in/juju/charmrepo.v2-unstable"
	"gopkg.in/juju/charmrepo.v2-unstable/csclient"
	"gopkg.in/macaroon-bakery.v1/httpbakery"
	"gopkg.in/macaroon.v1"

	"github.com/juju/juju/api"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/environs/config"
)

// maybeTermsAgreementError returns err as a *termsAgreementError
// if it has a "terms agreement required" error code, otherwise
// it returns err unchanged.
func maybeTermsAgreementError(err error) error {
	const code = "term agreement required"
	e, ok := errors.Cause(err).(*httpbakery.DischargeError)
	if !ok || e.Reason == nil || e.Reason.Code != code {
		return err
	}
	magicMarker := code + ":"
	index := strings.LastIndex(e.Reason.Message, magicMarker)
	if index == -1 {
		return err
	}
	return &termsRequiredError{strings.Fields(e.Reason.Message[index+len(magicMarker):])}
}

type termsRequiredError struct {
	Terms []string
}

func (e *termsRequiredError) Error() string {
	return fmt.Sprintf("please agree to terms %q", strings.Join(e.Terms, " "))
}

func isSeriesSupported(requestedSeries string, supportedSeries []string) bool {
	for _, series := range supportedSeries {
		if series == requestedSeries {
			return true
		}
	}
	return false
}

type resolveCharmStoreEntityParams struct {
	urlStr          string
	requestedSeries string
	forceSeries     bool
	csParams        charmrepo.NewCharmStoreParams
	repoPath        string
	conf            *config.Config
}

// resolveCharmStoreEntityURL resolves the given charm or bundle URL string
// by looking it up in the appropriate charm repository.
// If it is a charm store URL, the given csParams will
// be used to access the charm store repository.
// If it is a local charm or bundle URL, the local charm repository at
// the given repoPath will be used. The given configuration
// will be used to add any necessary attributes to the repo
// and to return the charm's supported series if possible.
//
// resolveCharmStoreEntityURL also returns the charm repository holding
// the charm or bundle.
func resolveCharmStoreEntityURL(args resolveCharmStoreEntityParams) (*charm.URL, []string, charmrepo.Interface, error) {
	url, err := charm.ParseURL(args.urlStr)
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}
	repo, err := charmrepo.InferRepository(url, args.csParams, args.repoPath)
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}
	repo = config.SpecializeCharmRepo(repo, args.conf)

	if url.Schema == "local" && url.Series == "" {
		if defaultSeries, ok := args.conf.DefaultSeries(); ok {
			url.Series = defaultSeries
		}
		if url.Series == "" {
			possibleURL := *url
			possibleURL.Series = config.LatestLtsSeries()
			logger.Errorf("The series is not specified in the model (default-series) or with the charm. Did you mean:\n\t%s", &possibleURL)
			return nil, nil, nil, errors.Errorf("cannot resolve series for charm: %q", url)
		}
	}
	resultUrl, supportedSeries, err := repo.Resolve(url)
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}
	return resultUrl, supportedSeries, repo, nil
}

// addCharmFromURL calls the appropriate client API calls to add the
// given charm URL to state. For non-public charm URLs, this function also
// handles the macaroon authorization process using the given csClient.
// The resulting charm URL of the added charm is displayed on stdout.
func addCharmFromURL(client *api.Client, curl *charm.URL, repo charmrepo.Interface, csclient *csClient) (*charm.URL, error) {
	switch curl.Schema {
	case "local":
		ch, err := repo.Get(curl)
		if err != nil {
			return nil, err
		}
		stateCurl, err := client.AddLocalCharm(curl, ch)
		if err != nil {
			return nil, err
		}
		curl = stateCurl
	case "cs":
		if err := client.AddCharm(curl); err != nil {
			if !params.IsCodeUnauthorized(err) {
				return nil, errors.Trace(err)
			}
			m, err := csclient.authorize(curl)
			if err != nil {
				return nil, maybeTermsAgreementError(err)
			}
			if err := client.AddCharmWithAuthorization(curl, m); err != nil {
				return nil, errors.Trace(err)
			}
		}
	default:
		return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
	}
	return curl, nil
}

// csClient gives access to the charm store server and provides parameters
// for connecting to the charm store.
type csClient struct {
	params charmrepo.NewCharmStoreParams
}

// newCharmStoreClient is called to obtain a charm store client
// including the parameters for connecting to the charm store, and
// helpers to save the local authorization cookies and to authorize
// non-public charm deployments. It is defined as a variable so it can
// be changed for testing purposes.
var newCharmStoreClient = func(client *http.Client) *csClient {
	return &csClient{
		params: charmrepo.NewCharmStoreParams{
			HTTPClient:   client,
			VisitWebPage: httpbakery.OpenWebBrowser,
		},
	}
}

// authorize acquires and return the charm store delegatable macaroon to be
// used to add the charm corresponding to the given URL.
// The macaroon is properly attenuated so that it can only be used to deploy
// the given charm URL.
func (c *csClient) authorize(curl *charm.URL) (*macaroon.Macaroon, error) {
	if curl == nil {
		return nil, errors.New("empty charm url not allowed")
	}

	client := csclient.New(csclient.Params{
		URL:          c.params.URL,
		HTTPClient:   c.params.HTTPClient,
		VisitWebPage: c.params.VisitWebPage,
	})
	endpoint := "/delegatable-macaroon"
	endpoint += "?id=" + url.QueryEscape(curl.String())

	var m *macaroon.Macaroon
	if err := client.Get(endpoint, &m); err != nil {
		return nil, errors.Trace(err)
	}

	return m, nil
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"

	"github.com/funkygao/gafka/mpool"
)

type SubHandler func(statusCode int, msg []byte) error

type Client struct {
	cf *Config

	conn *http.Client
	addr string
}

func NewClient(appId string, cf *Config) *Client {
	if cf == nil {
		cf = DefaultConfig()
	}
	cf.AppId = appId
	return &Client{
		cf: cf,
	}
}

func (this *Client) Connect(addr string) {
	this.addr = addr
	this.conn = &http.Client{
		Timeout: this.cf.Timeout,
		Transport: &http.Transport{
			MaxIdleConnsPerHost: 1,
			Proxy:               http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout: this.cf.Timeout,
				//KeepAlive: this.cf.KeepAlive, TODO
			}).Dial,
			DisableKeepAlives:     false, // enable http conn reuse
			ResponseHeaderTimeout: this.cf.Timeout,
			TLSHandshakeTimeout:   this.cf.Timeout,
		},
	}
}

func (this *Client) Close() {
	//	this.conn.Transport.RoundTrip().Close() TODO
}

// TODO async
func (this *Client) Publish(topic, ver, key string, msg []byte) (err error) {
	buf := mpool.BytesBufferGet()
	defer mpool.BytesBufferPut(buf)

	buf.Reset()
	buf.Write(msg)
	var req *http.Request
	url := fmt.Sprintf("%s/topics/%s/%s?key=%s", this.addr, topic, ver, key)
	req, err = http.NewRequest("POST", url, buf)
	if err != nil {
		return
	}

	if this.cf.Debug {
		log.Printf("pub: %s", url)
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Pubkey", this.cf.Secret)

	var response *http.Response
	response, err = this.conn.Do(req)
	if err != nil {
		return
	}

	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return
	}

	// reuse the connection
	response.Body.Close()

	if this.cf.Debug {
		log.Printf("got: %s", string(b))
	}

	return nil
}

func (this *Client) Subscribe(appid, topic, ver, group string, h SubHandler) error {
	url := fmt.Sprintf("%s/topics/%s/%s/%s/%s?limit=", this.addr,
		appid, topic, ver, group)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Subkey", this.cf.Secret)
	for {
		if this.cf.Debug {
			log.Printf("sub: %s", url)
		}

		response, err := this.conn.Do(req)
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}

		if this.cf.Debug {
			log.Printf("got: [%s] %s", response.Status, string(b))
		}

		// reuse the connection
		response.Body.Close()

		if err = h(response.StatusCode, b); err != nil {
			return err
		}
	}

}
<commit_msg>pub api should raise err when http 200 not found<commit_after>package api

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"

	"github.com/funkygao/gafka/mpool"
)

type SubHandler func(statusCode int, msg []byte) error

type Client struct {
	cf *Config

	conn *http.Client
	addr string
}

func NewClient(appId string, cf *Config) *Client {
	if cf == nil {
		cf = DefaultConfig()
	}
	cf.AppId = appId
	return &Client{
		cf: cf,
	}
}

func (this *Client) Connect(addr string) {
	this.addr = addr
	this.conn = &http.Client{
		Timeout: this.cf.Timeout,
		Transport: &http.Transport{
			MaxIdleConnsPerHost: 1,
			Proxy:               http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout: this.cf.Timeout,
				//KeepAlive: this.cf.KeepAlive, TODO
			}).Dial,
			DisableKeepAlives:     false, // enable http conn reuse
			ResponseHeaderTimeout: this.cf.Timeout,
			TLSHandshakeTimeout:   this.cf.Timeout,
		},
	}
}

func (this *Client) Close() {
	//	this.conn.Transport.RoundTrip().Close() TODO
}

// TODO async
func (this *Client) Publish(topic, ver, key string, msg []byte) (err error) {
	buf := mpool.BytesBufferGet()
	defer mpool.BytesBufferPut(buf)

	buf.Reset()
	buf.Write(msg)
	var req *http.Request
	url := fmt.Sprintf("%s/topics/%s/%s?key=%s", this.addr, topic, ver, key)
	req, err = http.NewRequest("POST", url, buf)
	if err != nil {
		return
	}

	if this.cf.Debug {
		log.Printf("pub: %s", url)
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Pubkey", this.cf.Secret)

	var response *http.Response
	response, err = this.conn.Do(req)
	if err != nil {
		return
	}

	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return
	}

	// reuse the connection
	response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return errors.New(string(b))
	}

	if this.cf.Debug {
		log.Printf("got: %s", string(b))
	}

	return nil
}

func (this *Client) Subscribe(appid, topic, ver, group string, h SubHandler) error {
	url := fmt.Sprintf("%s/topics/%s/%s/%s/%s?limit=", this.addr,
		appid, topic, ver, group)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Subkey", this.cf.Secret)
	for {
		if this.cf.Debug {
			log.Printf("sub: %s", url)
		}

		response, err := this.conn.Do(req)
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}

		if this.cf.Debug {
			log.Printf("got: [%s] %s", response.Status, string(b))
		}

		// reuse the connection
		response.Body.Close()

		if err = h(response.StatusCode, b); err != nil {
			return err
		}
	}

}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"

	"github.com/funkygao/gafka/mpool"
	"github.com/funkygao/gorequest"
)

type SubHandler func(statusCode int, msg []byte) error

type Client struct {
	cf *Config

	conn *http.Client
	addr string
}

func NewClient(appId string, cf *Config) *Client {
	if cf == nil {
		cf = DefaultConfig()
	}
	cf.AppId = appId
	return &Client{
		cf: cf,
	}
}

func (this *Client) Connect(addr string) {
	this.addr = addr
	this.conn = &http.Client{
		Timeout: this.cf.Timeout,
		Transport: &http.Transport{
			MaxIdleConnsPerHost: 1,
			Proxy:               http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout: this.cf.Timeout,
			}).Dial,
			DisableKeepAlives:     false, // enable http conn reuse
			ResponseHeaderTimeout: this.cf.Timeout,
			TLSHandshakeTimeout:   this.cf.Timeout,
		},
	}
}

func (this *Client) Close() {
	//	this.conn.Transport.RoundTrip().Close() TODO
}

// TODO async
func (this *Client) Publish(topic, ver, key string, msg []byte) (err error) {
	buf := mpool.BytesBufferGet()
	defer mpool.BytesBufferPut(buf)

	buf.Reset()
	buf.Write(msg)
	var req *http.Request
	url := fmt.Sprintf("%s/topics/%s/%s?key=%s", this.addr, topic, ver, key)
	req, err = http.NewRequest("POST", url, buf)
	if err != nil {
		return
	}

	if this.cf.Debug {
		log.Printf("pub: %s", url)
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Pubkey", this.cf.Secret)

	var response *http.Response
	response, err = this.conn.Do(req)
	if err != nil {
		return
	}

	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return
	}

	// reuse the connection
	response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		return errors.New(string(b))
	}

	if this.cf.Debug {
		log.Printf("got: %s", string(b))
	}

	return nil
}

func (this *Client) Subscribe(appid, topic, ver, group string, h SubHandler) error {
	url := fmt.Sprintf("%s/topics/%s/%s/%s?group=%s&limit=1", this.addr,
		appid, topic, ver, group)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Subkey", this.cf.Secret)
	for {
		if this.cf.Debug {
			log.Printf("sub: %s", url)
		}

		response, err := this.conn.Do(req)
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}

		if this.cf.Debug {
			log.Printf("got: [%s] %s", response.Status, string(b))
		}

		// reuse the connection
		response.Body.Close()

		if err = h(response.StatusCode, b); err != nil {
			return err
		}
	}

}

func (this *Client) AckedSubscribe(appid, topic, ver, group string, h SubHandler) error {
	url := fmt.Sprintf("%s/topics/%s/%s/%s?group=%s&ack=1", this.addr,
		appid, topic, ver, group)
	req := gorequest.New()
	req.Get(url).Set("AppId", this.cf.AppId).Set("Subkey", this.cf.Secret)
	for {
		response, b, errs := req.EndBytes()
		if len(errs) > 0 {
			return errs[0]
		}

		req.Set("X-Partition", response.Header.Get("X-Partition"))
		req.Set("X-Offset", response.Header.Get("X-Offset"))

		if err := h(response.StatusCode, b); err != nil {
			return err
		}
	}

}
<commit_msg>in debug mode, echo the http Header data<commit_after>package api

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"

	"github.com/funkygao/gafka/mpool"
	"github.com/funkygao/gorequest"
)

type SubHandler func(statusCode int, msg []byte) error

type Client struct {
	cf *Config

	conn *http.Client
	addr string
}

func NewClient(appId string, cf *Config) *Client {
	if cf == nil {
		cf = DefaultConfig()
	}
	cf.AppId = appId
	return &Client{
		cf: cf,
	}
}

func (this *Client) Connect(addr string) {
	this.addr = addr
	this.conn = &http.Client{
		Timeout: this.cf.Timeout,
		Transport: &http.Transport{
			MaxIdleConnsPerHost: 1,
			Proxy:               http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout: this.cf.Timeout,
			}).Dial,
			DisableKeepAlives:     false, // enable http conn reuse
			ResponseHeaderTimeout: this.cf.Timeout,
			TLSHandshakeTimeout:   this.cf.Timeout,
		},
	}
}

func (this *Client) Close() {
	//	this.conn.Transport.RoundTrip().Close() TODO
}

// TODO async
func (this *Client) Publish(topic, ver, key string, msg []byte) (err error) {
	buf := mpool.BytesBufferGet()
	defer mpool.BytesBufferPut(buf)

	buf.Reset()
	buf.Write(msg)
	var req *http.Request
	url := fmt.Sprintf("%s/topics/%s/%s?key=%s", this.addr, topic, ver, key)
	req, err = http.NewRequest("POST", url, buf)
	if err != nil {
		return
	}

	if this.cf.Debug {
		log.Printf("pub: %s", url)
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Pubkey", this.cf.Secret)

	var response *http.Response
	response, err = this.conn.Do(req)
	if err != nil {
		return
	}

	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return
	}

	// reuse the connection
	response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		return errors.New(string(b))
	}

	if this.cf.Debug {
		log.Printf("got: %s", string(b))
	}

	return nil
}

func (this *Client) Subscribe(appid, topic, ver, group string, h SubHandler) error {
	url := fmt.Sprintf("%s/topics/%s/%s/%s?group=%s&limit=1", this.addr,
		appid, topic, ver, group)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	req.Header.Set("AppId", this.cf.AppId)
	req.Header.Set("Subkey", this.cf.Secret)
	for {
		if this.cf.Debug {
			log.Printf("sub: %s", url)
		}

		response, err := this.conn.Do(req)
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}

		if this.cf.Debug {
			log.Printf("--> [%s] %s", response.Status, string(b))
			log.Printf("Partition: %s, Offset: %s", response.Header.Get("X-Partition"),
				response.Header.Get("X-Offset"))
		}

		// reuse the connection
		response.Body.Close()

		if err = h(response.StatusCode, b); err != nil {
			return err
		}
	}

}

func (this *Client) AckedSubscribe(appid, topic, ver, group string, h SubHandler) error {
	url := fmt.Sprintf("%s/topics/%s/%s/%s?group=%s&ack=1", this.addr,
		appid, topic, ver, group)
	req := gorequest.New()
	req.Get(url).Set("AppId", this.cf.AppId).Set("Subkey", this.cf.Secret)
	for {
		response, b, errs := req.EndBytes()
		if len(errs) > 0 {
			return errs[0]
		}

		if this.cf.Debug {
			log.Printf("--> [%s] %s", response.Status, string(b))
			log.Printf("Partition: %s, Offset: %s", response.Header.Get("X-Partition"),
				response.Header.Get("X-Offset"))
		}

		req.Set("X-Partition", response.Header.Get("X-Partition"))
		req.Set("X-Offset", response.Header.Get("X-Offset"))
		if err := h(response.StatusCode, b); err != nil {
			return err
		}
	}

}
<|endoftext|>


<commit_before>package agent

import (
	"github.intel.com/hpdd/lustre"
	"github.intel.com/hpdd/lustre/fs"
	"github.intel.com/hpdd/lustre/pkg/xattr"
)

const xattrFileID = "trusted.hsm_file_id"

func updateFileID(mnt fs.RootDir, fid *lustre.Fid, fileID []byte) error {
	p := fs.FidPath(mnt, fid)

	err := xattr.Lsetxattr(p, xattrFileID, fileID, 0)
	if err != nil {
		return err
	}
	return nil

}

func getFileID(mnt fs.RootDir, fid *lustre.Fid) ([]byte, error) {
	buf := make([]byte, 256)
	p := fs.FidPath(mnt, fid)

	sz, err := xattr.Lgetxattr(p, xattrFileID, buf)
	if err != nil {
		return nil, err
	}
	return buf[0:sz], nil
}
<commit_msg>Add debug to fileID stuff<commit_after>package agent

import (
	"github.intel.com/hpdd/logging/debug"
	"github.intel.com/hpdd/lustre"
	"github.intel.com/hpdd/lustre/fs"
	"github.intel.com/hpdd/lustre/pkg/xattr"
)

const xattrFileID = "trusted.hsm_file_id"

func updateFileID(mnt fs.RootDir, fid *lustre.Fid, fileID []byte) error {
	p := fs.FidPath(mnt, fid)

	debug.Printf("setting %s=%s on %s", xattrFileID, fileID, p)
	return xattr.Lsetxattr(p, xattrFileID, fileID, 0)

}

func getFileID(mnt fs.RootDir, fid *lustre.Fid) ([]byte, error) {
	buf := make([]byte, 256)
	p := fs.FidPath(mnt, fid)

	sz, err := xattr.Lgetxattr(p, xattrFileID, buf)
	if err != nil {
		return nil, err
	}
	return buf[0:sz], nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"k8s.io/klog/v2"
	cmdConfig "k8s.io/minikube/cmd/minikube/cmd/config"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/image"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/node"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
)

// cacheImageConfigKey is the config field name used to store which images we have previously cached
const cacheImageConfigKey = "cache"

const allFlag = "all"

// cacheCmd represents the cache command
var cacheCmd = &cobra.Command{
	Use:   "cache",
	Short: "Add, delete, or push a local image into minikube",
	Long:  "Add, delete, or push a local image into minikube",
}

// addCacheCmd represents the cache add command
var addCacheCmd = &cobra.Command{
	Use:   "add",
	Short: "Add an image to local cache.",
	Long:  "Add an image to local cache.",
	Run: func(cmd *cobra.Command, args []string) {
		out.WarningT("\"minikube cache\" will be deprecated in upcoming versions, please switch to \"minikube image load\"")
		// Cache and load images into docker daemon
		if err := machine.CacheAndLoadImages(args, cacheAddProfiles(), false); err != nil {
			exit.Error(reason.InternalCacheLoad, "Failed to cache and load images", err)
		}
		// Add images to config file
		if err := cmdConfig.AddToConfigMap(cacheImageConfigKey, args); err != nil {
			exit.Error(reason.InternalAddConfig, "Failed to update config", err)
		}
	},
}

func addCacheCmdFlags() {
	addCacheCmd.Flags().Bool(allFlag, false, "Add image to cache for all running minikube clusters")
}

func cacheAddProfiles() []*config.Profile {
	if viper.GetBool(allFlag) {
		validProfiles, _, err := config.ListProfiles() // need to load image to all profiles
		if err != nil {
			klog.Warningf("error listing profiles: %v", err)
		}
		return validProfiles
	}
	profile := viper.GetString(config.ProfileName)
	p, err := config.LoadProfile(profile)
	if err != nil {
		exit.Message(reason.Usage, "{{.profile}} profile is not valid: {{.err}}", out.V{"profile": profile, "err": err})
	}
	return []*config.Profile{p}
}

// deleteCacheCmd represents the cache delete command
var deleteCacheCmd = &cobra.Command{
	Use:   "delete",
	Short: "Delete an image from the local cache.",
	Long:  "Delete an image from the local cache.",
	Run: func(cmd *cobra.Command, args []string) {
		// Delete images from config file
		if err := cmdConfig.DeleteFromConfigMap(cacheImageConfigKey, args); err != nil {
			exit.Error(reason.InternalDelConfig, "Failed to delete images from config", err)
		}
		// Delete images from cache/images directory
		if err := image.DeleteFromCacheDir(args); err != nil {
			exit.Error(reason.HostDelCache, "Failed to delete images", err)
		}
	},
}

// reloadCacheCmd represents the cache reload command
var reloadCacheCmd = &cobra.Command{
	Use:   "reload",
	Short: "reload cached images.",
	Long:  "reloads images previously added using the 'cache add' subcommand",
	Run: func(cmd *cobra.Command, args []string) {
		err := node.CacheAndLoadImagesInConfig(cacheAddProfiles())
		if err != nil {
			exit.Error(reason.GuestCacheLoad, "Failed to reload cached images", err)
		}
	},
}

func init() {
	addCacheCmdFlags()
	cacheCmd.AddCommand(addCacheCmd)
	cacheCmd.AddCommand(deleteCacheCmd)
	cacheCmd.AddCommand(reloadCacheCmd)
}
<commit_msg>Improve description for minikube cache command<commit_after>/*
Copyright 2017 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"k8s.io/klog/v2"
	cmdConfig "k8s.io/minikube/cmd/minikube/cmd/config"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/image"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/node"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
)

// cacheImageConfigKey is the config field name used to store which images we have previously cached
const cacheImageConfigKey = "cache"

const allFlag = "all"

// cacheCmd represents the cache command
var cacheCmd = &cobra.Command{
	Use:   "cache",
	Short: "Manage cache for images",
	Long:  "Add an image into minikube as a local cache, or delete, reload the cached images",
}

// addCacheCmd represents the cache add command
var addCacheCmd = &cobra.Command{
	Use:   "add",
	Short: "Add an image to local cache.",
	Long:  "Add an image to local cache.",
	Run: func(cmd *cobra.Command, args []string) {
		out.WarningT("\"minikube cache\" will be deprecated in upcoming versions, please switch to \"minikube image load\"")
		// Cache and load images into docker daemon
		if err := machine.CacheAndLoadImages(args, cacheAddProfiles(), false); err != nil {
			exit.Error(reason.InternalCacheLoad, "Failed to cache and load images", err)
		}
		// Add images to config file
		if err := cmdConfig.AddToConfigMap(cacheImageConfigKey, args); err != nil {
			exit.Error(reason.InternalAddConfig, "Failed to update config", err)
		}
	},
}

func addCacheCmdFlags() {
	addCacheCmd.Flags().Bool(allFlag, false, "Add image to cache for all running minikube clusters")
}

func cacheAddProfiles() []*config.Profile {
	if viper.GetBool(allFlag) {
		validProfiles, _, err := config.ListProfiles() // need to load image to all profiles
		if err != nil {
			klog.Warningf("error listing profiles: %v", err)
		}
		return validProfiles
	}
	profile := viper.GetString(config.ProfileName)
	p, err := config.LoadProfile(profile)
	if err != nil {
		exit.Message(reason.Usage, "{{.profile}} profile is not valid: {{.err}}", out.V{"profile": profile, "err": err})
	}
	return []*config.Profile{p}
}

// deleteCacheCmd represents the cache delete command
var deleteCacheCmd = &cobra.Command{
	Use:   "delete",
	Short: "Delete an image from the local cache.",
	Long:  "Delete an image from the local cache.",
	Run: func(cmd *cobra.Command, args []string) {
		// Delete images from config file
		if err := cmdConfig.DeleteFromConfigMap(cacheImageConfigKey, args); err != nil {
			exit.Error(reason.InternalDelConfig, "Failed to delete images from config", err)
		}
		// Delete images from cache/images directory
		if err := image.DeleteFromCacheDir(args); err != nil {
			exit.Error(reason.HostDelCache, "Failed to delete images", err)
		}
	},
}

// reloadCacheCmd represents the cache reload command
var reloadCacheCmd = &cobra.Command{
	Use:   "reload",
	Short: "reload cached images.",
	Long:  "reloads images previously added using the 'cache add' subcommand",
	Run: func(cmd *cobra.Command, args []string) {
		err := node.CacheAndLoadImagesInConfig(cacheAddProfiles())
		if err != nil {
			exit.Error(reason.GuestCacheLoad, "Failed to reload cached images", err)
		}
	},
}

func init() {
	addCacheCmdFlags()
	cacheCmd.AddCommand(addCacheCmd)
	cacheCmd.AddCommand(deleteCacheCmd)
	cacheCmd.AddCommand(reloadCacheCmd)
}
<|endoftext|>