<commit_before>package services

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/die-net/lrucache"
	"github.com/gregjones/httpcache"
	"github.com/matrix-org/go-neb/database"
	"github.com/matrix-org/go-neb/matrix"
	"github.com/matrix-org/go-neb/polling"
	"github.com/matrix-org/go-neb/types"
	"github.com/mmcdole/gofeed"
	"github.com/prometheus/client_golang/prometheus"
	"html"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

var cachingClient *http.Client

var (
	pollCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "goneb_rss_polls_total",
		Help: "The number of feed polls from RSS services",
	}, []string{"url", "http_status"})
)

const minPollingIntervalSeconds = 60 * 5 // 5 min (News feeds can be genuinely spammy)

type rssBotService struct {
	types.DefaultService
	id            string
	serviceUserID string
	Feeds         map[string]struct { // feed_url => { }
		PollIntervalMins         int      `json:"poll_interval_mins"`
		Rooms                    []string `json:"rooms"`
		NextPollTimestampSecs    int64    // Internal: When we should poll again
		FeedUpdatedTimestampSecs int64    // Internal: The last time the feed was updated
		RecentGUIDs              []string // Internal: The most recently seen GUIDs. Sized to the number of items in the feed.
	} `json:"feeds"`
}

func (s *rssBotService) ServiceUserID() string { return s.serviceUserID }
func (s *rssBotService) ServiceID() string     { return s.id }
func (s *rssBotService) ServiceType() string   { return "rssbot" }

// Register will check the liveness of each RSS feed given. If all feeds check out okay, no error is returned.
func (s *rssBotService) Register(oldService types.Service, client *matrix.Client) error {
	if len(s.Feeds) == 0 {
		// this is an error UNLESS the old service had some feeds in which case they are deleting us :(
		var numOldFeeds int
		oldFeedService, ok := oldService.(*rssBotService)
		if !ok {
			log.WithField("service", oldService).Error("Old service isn't a rssBotService")
		} else {
			numOldFeeds = len(oldFeedService.Feeds)
		}
		if numOldFeeds == 0 {
			return errors.New("An RSS feed must be specified.")
		}
		return nil
	}
	// Make sure we can parse the feed
	for feedURL, feedInfo := range s.Feeds {
		fp := gofeed.NewParser()
		fp.Client = cachingClient
		if _, err := fp.ParseURL(feedURL); err != nil {
			return fmt.Errorf("Failed to read URL %s: %s", feedURL, err.Error())
		}
		if len(feedInfo.Rooms) == 0 {
			return fmt.Errorf("Feed %s has no rooms to send updates to", feedURL)
		}
	}

	s.joinRooms(client)
	return nil
}

func (s *rssBotService) joinRooms(client *matrix.Client) {
	roomSet := make(map[string]bool)
	for _, feedInfo := range s.Feeds {
		for _, roomID := range feedInfo.Rooms {
			roomSet[roomID] = true
		}
	}

	for roomID := range roomSet {
		if _, err := client.JoinRoom(roomID, "", ""); err != nil {
			log.WithFields(log.Fields{
				log.ErrorKey: err,
				"room_id":    roomID,
				"user_id":    client.UserID,
			}).Error("Failed to join room")
		}
	}
}

func (s *rssBotService) PostRegister(oldService types.Service) {
	if len(s.Feeds) == 0 { // bye-bye :(
		logger := log.WithFields(log.Fields{
			"service_id":   s.ServiceID(),
			"service_type": s.ServiceType(),
		})
		logger.Info("Deleting service: No feeds remaining.")
		polling.StopPolling(s)
		if err := database.GetServiceDB().DeleteService(s.ServiceID()); err != nil {
			logger.WithError(err).Error("Failed to delete service")
		}
	}
}

func (s *rssBotService) OnPoll(cli *matrix.Client) time.Time {
	logger := log.WithFields(log.Fields{
		"service_id":   s.ServiceID(),
		"service_type": s.ServiceType(),
	})
	now := time.Now().Unix() // Second resolution

	// Work out which feeds should be polled
	var pollFeeds []string
	for u, feedInfo := range s.Feeds {
		if feedInfo.NextPollTimestampSecs == 0 || now >= feedInfo.NextPollTimestampSecs {
			// re-query this feed
			pollFeeds = append(pollFeeds, u)
		}
	}

	if len(pollFeeds) == 0 {
		return s.nextTimestamp()
	}

	// Query each feed and send new items to subscribed rooms
	for _, u := range pollFeeds {
		feed, items, err := s.queryFeed(u)
		if err != nil {
			logger.WithField("feed_url", u).WithError(err).Error("Failed to query feed")
			incrementMetrics(u, err)
			continue
		}
		incrementMetrics(u, nil)
		// Loop backwards since [0] is the most recent and we want to send in chronological order
		for i := len(items) - 1; i >= 0; i-- {
			item := items[i]
			if err := s.sendToRooms(cli, u, feed, item); err != nil {
				logger.WithFields(log.Fields{
					"feed_url":   u,
					log.ErrorKey: err,
					"item":       item,
				}).Error("Failed to send item to room")
			}
		}
	}

	// Persist the service to save the next poll times
	if _, err := database.GetServiceDB().StoreService(s); err != nil {
		logger.WithError(err).Error("Failed to persist next poll times for service")
	}

	return s.nextTimestamp()
}

func incrementMetrics(urlStr string, err error) {
	// extract domain part of RSS feed URL to get coarser (more useful) statistics
	domain := urlStr
	u, urlErr := url.Parse(urlStr)
	if urlErr == nil {
		domain = u.Host
	}
	if err != nil {
		herr, ok := err.(gofeed.HTTPError)
		statusCode := 0 // e.g. network timeout
		if ok {
			statusCode = herr.StatusCode
		}
		pollCounter.With(prometheus.Labels{"url": domain, "http_status": strconv.Itoa(statusCode)}).Inc()
	} else {
		pollCounter.With(prometheus.Labels{"url": domain, "http_status": "200"}).Inc() // technically 2xx but gofeed doesn't tell us which
	}
}

func (s *rssBotService) nextTimestamp() time.Time {
	// return the earliest next poll ts
	var earliestNextTs int64
	for _, feedInfo := range s.Feeds {
		if earliestNextTs == 0 || feedInfo.NextPollTimestampSecs < earliestNextTs {
			earliestNextTs = feedInfo.NextPollTimestampSecs
		}
	}

	// Don't allow times in the past. Set a min re-poll threshold of 20s to avoid
	// tight-looping on feeds which 500.
	now := time.Now().Unix()
	if earliestNextTs <= now {
		earliestNextTs = now + 20
	}

	return time.Unix(earliestNextTs, 0)
}

// Query the given feed, update relevant timestamps and return NEW items
func (s *rssBotService) queryFeed(feedURL string) (*gofeed.Feed, []gofeed.Item, error) {
	log.WithField("feed_url", feedURL).Info("Querying feed")
	var items []gofeed.Item
	fp := gofeed.NewParser()
	fp.Client = cachingClient
	feed, err := fp.ParseURL(feedURL)
	if err != nil {
		return nil, items, err
	}

	// Work out which items are new, if any (based on the last updated TS we have)
	// If the TS is 0 then this is the first ever poll, so let's not send 10s of events
	// into the room and just do new ones from this point onwards.
	if s.Feeds[feedURL].FeedUpdatedTimestampSecs != 0 {
		items = s.newItems(feedURL, feed.Items)
	}

	now := time.Now().Unix() // Second resolution

	// Work out when this feed was last updated
	var feedLastUpdatedTs int64
	if feed.UpdatedParsed != nil {
		feedLastUpdatedTs = feed.UpdatedParsed.Unix()
	} else if len(feed.Items) > 0 {
		i := feed.Items[0]
		if i != nil && i.PublishedParsed != nil {
			feedLastUpdatedTs = i.PublishedParsed.Unix()
		}
	}

	// Work out when to next poll this feed
	nextPollTsSec := now + minPollingIntervalSeconds
	if s.Feeds[feedURL].PollIntervalMins > int(minPollingIntervalSeconds/60) {
		nextPollTsSec = now + int64(s.Feeds[feedURL].PollIntervalMins*60)
	}
	// TODO: Handle the 'sy' Syndication extension to control update interval.
	// See http://www.feedforall.com/syndication.htm and http://web.resource.org/rss/1.0/modules/syndication/

	s.updateFeedInfo(feedURL, feed.Items, nextPollTsSec, feedLastUpdatedTs)
	return feed, items, nil
}

func (s *rssBotService) newItems(feedURL string, allItems []*gofeed.Item) (items []gofeed.Item) {
	for _, i := range allItems {
		if i == nil || i.PublishedParsed == nil {
			continue
		}

		if i.PublishedParsed.Unix() > s.Feeds[feedURL].FeedUpdatedTimestampSecs {
			// if we've seen this guid before, we've sent it before (even if the timestamp is newer)
			seenBefore := false
			for _, guid := range s.Feeds[feedURL].RecentGUIDs {
				if guid == i.GUID {
					seenBefore = true
					break
				}
			}
			if seenBefore {
				continue
			}

			items = append(items, *i)
		}
	}
	return
}

func (s *rssBotService) updateFeedInfo(feedURL string, allFeedItems []*gofeed.Item, nextPollTs, feedUpdatedTs int64) {
	// map items to guid strings
	var guids []string
	for _, i := range allFeedItems {
		guids = append(guids, i.GUID)
	}

	for u := range s.Feeds {
		if u != feedURL {
			continue
		}
		f := s.Feeds[u]
		f.NextPollTimestampSecs = nextPollTs
		f.FeedUpdatedTimestampSecs = feedUpdatedTs
		f.RecentGUIDs = guids
		s.Feeds[u] = f
	}
}

func (s *rssBotService) sendToRooms(cli *matrix.Client, feedURL string, feed *gofeed.Feed, item gofeed.Item) error {
	logger := log.WithField("feed_url", feedURL).WithField("title", item.Title)
	logger.Info("New feed item")
	for _, roomID := range s.Feeds[feedURL].Rooms {
		if _, err := cli.SendMessageEvent(roomID, "m.room.message", itemToHTML(feed, item)); err != nil {
			logger.WithError(err).WithField("room_id", roomID).Error("Failed to send to room")
		}
	}
	return nil
}

// SomeOne posted a new article: Title Of The Entry ( https://someurl.com/blag )
func itemToHTML(feed *gofeed.Feed, item gofeed.Item) matrix.HTMLMessage {
	return matrix.GetHTMLMessage("m.notice", fmt.Sprintf(
		"%s posted a new article: %s ( %s )",
		html.EscapeString(feed.Title), html.EscapeString(item.Title), html.EscapeString(item.Link),
	))
}

type userAgentRoundTripper struct {
	Transport http.RoundTripper
}

func (rt userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", "Go-NEB")
	return rt.Transport.RoundTrip(req)
}

func init() {
	lruCache := lrucache.New(1024*1024*20, 0) // 20 MB cache, no max-age
	cachingClient = &http.Client{
		Transport: userAgentRoundTripper{httpcache.NewTransport(lruCache)},
	}
	types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
		r := &rssBotService{
			id:            serviceID,
			serviceUserID: serviceUserID,
		}
		return r
	})
	prometheus.MustRegister(pollCounter)
}
<commit_msg>Fix RSS feeds which do not return GUIDs and do not have published dates<commit_after>package services

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/die-net/lrucache"
	"github.com/gregjones/httpcache"
	"github.com/matrix-org/go-neb/database"
	"github.com/matrix-org/go-neb/matrix"
	"github.com/matrix-org/go-neb/polling"
	"github.com/matrix-org/go-neb/types"
	"github.com/mmcdole/gofeed"
	"github.com/prometheus/client_golang/prometheus"
	"html"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

var cachingClient *http.Client

var (
	pollCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "goneb_rss_polls_total",
		Help: "The number of feed polls from RSS services",
	}, []string{"url", "http_status"})
)

const minPollingIntervalSeconds = 60 * 5 // 5 min (News feeds can be genuinely spammy)

type rssBotService struct {
	types.DefaultService
	id            string
	serviceUserID string
	Feeds         map[string]struct { // feed_url => { }
		PollIntervalMins         int      `json:"poll_interval_mins"`
		Rooms                    []string `json:"rooms"`
		NextPollTimestampSecs    int64    // Internal: When we should poll again
		FeedUpdatedTimestampSecs int64    // Internal: The last time the feed was updated
		RecentGUIDs              []string // Internal: The most recently seen GUIDs. Sized to the number of items in the feed.
	} `json:"feeds"`
}

func (s *rssBotService) ServiceUserID() string { return s.serviceUserID }
func (s *rssBotService) ServiceID() string     { return s.id }
func (s *rssBotService) ServiceType() string   { return "rssbot" }

// Register will check the liveness of each RSS feed given. If all feeds check out okay, no error is returned.
func (s *rssBotService) Register(oldService types.Service, client *matrix.Client) error {
	if len(s.Feeds) == 0 {
		// this is an error UNLESS the old service had some feeds in which case they are deleting us :(
		var numOldFeeds int
		oldFeedService, ok := oldService.(*rssBotService)
		if !ok {
			log.WithField("service", oldService).Error("Old service isn't a rssBotService")
		} else {
			numOldFeeds = len(oldFeedService.Feeds)
		}
		if numOldFeeds == 0 {
			return errors.New("An RSS feed must be specified.")
		}
		return nil
	}
	// Make sure we can parse the feed
	for feedURL, feedInfo := range s.Feeds {
		fp := gofeed.NewParser()
		fp.Client = cachingClient
		if _, err := fp.ParseURL(feedURL); err != nil {
			return fmt.Errorf("Failed to read URL %s: %s", feedURL, err.Error())
		}
		if len(feedInfo.Rooms) == 0 {
			return fmt.Errorf("Feed %s has no rooms to send updates to", feedURL)
		}
	}

	s.joinRooms(client)
	return nil
}

func (s *rssBotService) joinRooms(client *matrix.Client) {
	roomSet := make(map[string]bool)
	for _, feedInfo := range s.Feeds {
		for _, roomID := range feedInfo.Rooms {
			roomSet[roomID] = true
		}
	}

	for roomID := range roomSet {
		if _, err := client.JoinRoom(roomID, "", ""); err != nil {
			log.WithFields(log.Fields{
				log.ErrorKey: err,
				"room_id":    roomID,
				"user_id":    client.UserID,
			}).Error("Failed to join room")
		}
	}
}

func (s *rssBotService) PostRegister(oldService types.Service) {
	if len(s.Feeds) == 0 { // bye-bye :(
		logger := log.WithFields(log.Fields{
			"service_id":   s.ServiceID(),
			"service_type": s.ServiceType(),
		})
		logger.Info("Deleting service: No feeds remaining.")
		polling.StopPolling(s)
		if err := database.GetServiceDB().DeleteService(s.ServiceID()); err != nil {
			logger.WithError(err).Error("Failed to delete service")
		}
	}
}

func (s *rssBotService) OnPoll(cli *matrix.Client) time.Time {
	logger := log.WithFields(log.Fields{
		"service_id":   s.ServiceID(),
		"service_type": s.ServiceType(),
	})
	now := time.Now().Unix() // Second resolution

	// Work out which feeds should be polled
	var pollFeeds []string
	for u, feedInfo := range s.Feeds {
		if feedInfo.NextPollTimestampSecs == 0 || now >= feedInfo.NextPollTimestampSecs {
			// re-query this feed
			pollFeeds = append(pollFeeds, u)
		}
	}

	if len(pollFeeds) == 0 {
		return s.nextTimestamp()
	}

	// Query each feed and send new items to subscribed rooms
	for _, u := range pollFeeds {
		feed, items, err := s.queryFeed(u)
		if err != nil {
			logger.WithField("feed_url", u).WithError(err).Error("Failed to query feed")
			incrementMetrics(u, err)
			continue
		}
		incrementMetrics(u, nil)
		// Loop backwards since [0] is the most recent and we want to send in chronological order
		for i := len(items) - 1; i >= 0; i-- {
			item := items[i]
			if err := s.sendToRooms(cli, u, feed, item); err != nil {
				logger.WithFields(log.Fields{
					"feed_url":   u,
					log.ErrorKey: err,
					"item":       item,
				}).Error("Failed to send item to room")
			}
		}
	}

	// Persist the service to save the next poll times
	if _, err := database.GetServiceDB().StoreService(s); err != nil {
		logger.WithError(err).Error("Failed to persist next poll times for service")
	}

	return s.nextTimestamp()
}

func incrementMetrics(urlStr string, err error) {
	// extract domain part of RSS feed URL to get coarser (more useful) statistics
	domain := urlStr
	u, urlErr := url.Parse(urlStr)
	if urlErr == nil {
		domain = u.Host
	}
	if err != nil {
		herr, ok := err.(gofeed.HTTPError)
		statusCode := 0 // e.g. network timeout
		if ok {
			statusCode = herr.StatusCode
		}
		pollCounter.With(prometheus.Labels{"url": domain, "http_status": strconv.Itoa(statusCode)}).Inc()
	} else {
		pollCounter.With(prometheus.Labels{"url": domain, "http_status": "200"}).Inc() // technically 2xx but gofeed doesn't tell us which
	}
}

func (s *rssBotService) nextTimestamp() time.Time {
	// return the earliest next poll ts
	var earliestNextTs int64
	for _, feedInfo := range s.Feeds {
		if earliestNextTs == 0 || feedInfo.NextPollTimestampSecs < earliestNextTs {
			earliestNextTs = feedInfo.NextPollTimestampSecs
		}
	}

	// Don't allow times in the past. Set a min re-poll threshold of 20s to avoid
	// tight-looping on feeds which 500.
	now := time.Now().Unix()
	if earliestNextTs <= now {
		earliestNextTs = now + 20
	}

	return time.Unix(earliestNextTs, 0)
}

// Query the given feed, update relevant timestamps and return NEW items
func (s *rssBotService) queryFeed(feedURL string) (*gofeed.Feed, []gofeed.Item, error) {
	log.WithField("feed_url", feedURL).Info("Querying feed")
	var items []gofeed.Item
	fp := gofeed.NewParser()
	fp.Client = cachingClient
	feed, err := fp.ParseURL(feedURL)
	if err != nil {
		return nil, items, err
	}

	// Patch up the item list: make sure each item has a GUID.
	for idx := 0; idx < len(feed.Items); idx++ {
		itm := feed.Items[idx]
		if itm.GUID == "" {
			if itm.Link != "" {
				itm.GUID = itm.Link
			} else if itm.Title != "" {
				itm.GUID = itm.Title
			}
			feed.Items[idx] = itm
		}
	}

	// Work out which items are new, if any (based on the last updated TS we have)
	// If the TS is 0 then this is the first ever poll, so let's not send 10s of events
	// into the room and just do new ones from this point onwards.
	if s.Feeds[feedURL].FeedUpdatedTimestampSecs != 0 {
		items = s.newItems(feedURL, feed.Items)
	}

	now := time.Now().Unix() // Second resolution

	// Work out when this feed was last updated
	var feedLastUpdatedTs int64
	if feed.UpdatedParsed != nil {
		feedLastUpdatedTs = feed.UpdatedParsed.Unix()
	} else if len(feed.Items) > 0 {
		i := feed.Items[0]
		if i != nil && i.PublishedParsed != nil {
			feedLastUpdatedTs = i.PublishedParsed.Unix()
		} else {
			feedLastUpdatedTs = time.Now().Unix()
		}
	}

	// Work out when to next poll this feed
	nextPollTsSec := now + minPollingIntervalSeconds
	if s.Feeds[feedURL].PollIntervalMins > int(minPollingIntervalSeconds/60) {
		nextPollTsSec = now + int64(s.Feeds[feedURL].PollIntervalMins*60)
	}
	// TODO: Handle the 'sy' Syndication extension to control update interval.
	// See http://www.feedforall.com/syndication.htm and http://web.resource.org/rss/1.0/modules/syndication/

	s.updateFeedInfo(feedURL, feed.Items, nextPollTsSec, feedLastUpdatedTs)
	return feed, items, nil
}

func (s *rssBotService) newItems(feedURL string, allItems []*gofeed.Item) (items []gofeed.Item) {
	for _, i := range allItems {
		if i == nil {
			continue
		}
		// if we've seen this guid before, we've sent it before
		seenBefore := false
		for _, guid := range s.Feeds[feedURL].RecentGUIDs {
			if guid == i.GUID {
				seenBefore = true
				break
			}
		}
		if seenBefore {
			continue
		}

		items = append(items, *i)
	}
	return
}

func (s *rssBotService) updateFeedInfo(feedURL string, allFeedItems []*gofeed.Item, nextPollTs, feedUpdatedTs int64) {
	// map items to guid strings
	var guids []string
	for _, i := range allFeedItems {
		guids = append(guids, i.GUID)
	}

	for u := range s.Feeds {
		if u != feedURL {
			continue
		}
		f := s.Feeds[u]
		f.NextPollTimestampSecs = nextPollTs
		f.FeedUpdatedTimestampSecs = feedUpdatedTs
		f.RecentGUIDs = guids
		s.Feeds[u] = f
	}
}

func (s *rssBotService) sendToRooms(cli *matrix.Client, feedURL string, feed *gofeed.Feed, item gofeed.Item) error {
	logger := log.WithField("feed_url", feedURL).WithField("title", item.Title)
	logger.Info("New feed item")
	for _, roomID := range s.Feeds[feedURL].Rooms {
		if _, err := cli.SendMessageEvent(roomID, "m.room.message", itemToHTML(feed, item)); err != nil {
			logger.WithError(err).WithField("room_id", roomID).Error("Failed to send to room")
		}
	}
	return nil
}

// SomeOne posted a new article: Title Of The Entry ( https://someurl.com/blag )
func itemToHTML(feed *gofeed.Feed, item gofeed.Item) matrix.HTMLMessage {
	return matrix.GetHTMLMessage("m.notice", fmt.Sprintf(
		"%s posted a new article: %s ( %s )",
		html.EscapeString(feed.Title), html.EscapeString(item.Title), html.EscapeString(item.Link),
	))
}

type userAgentRoundTripper struct {
	Transport http.RoundTripper
}

func (rt userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", "Go-NEB")
	return rt.Transport.RoundTrip(req)
}

func init() {
	lruCache := lrucache.New(1024*1024*20, 0) // 20 MB cache, no max-age
	cachingClient = &http.Client{
		Transport: userAgentRoundTripper{httpcache.NewTransport(lruCache)},
	}
	types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
		r := &rssBotService{
			id:            serviceID,
			serviceUserID: serviceUserID,
		}
		return r
	})
	prometheus.MustRegister(pollCounter)
}
<|endoftext|>


<commit_before>package ssh

import (
	"errors"
	"fmt"
	"github.com/dcu/go-authy"
	"net/url"
	"strconv"
)

// User is a struct contains the user's info.
type User struct {
	Username    string
	PublicKeys  []string
	Email       string
	CountryCode int
	PhoneNumber string
	AuthyID     string
}

// NewUser returns a new instance of User
func NewUser(username string) *User {
	user := &User{
		Username: username,
	}
	return user
}

// LoadUser loads user data from the Authy API
func (user *User) LoadUser(authyId string) error {
	if len(authyId) == 0 {
		return errors.New("Invalid Authy ID.")
	}

	config, err := LoadConfig()
	if err != nil {
		return err
	}

	api := authy.NewAuthyAPI(config.APIKey)
	api.BaseURL = "https://api.authy.com"

	authyUser, err := api.UserStatus(authyId, url.Values{})
	if err != nil {
		return err
	}

	user.AuthyID = authyUser.ID
	user.CountryCode = authyUser.StatusData.Country
	user.PhoneNumber = authyUser.StatusData.PhoneNumber

	return nil
}

// Save saves the user
func Save() bool {
	return false
}

// CountryCodeStr returns the country code as a string.
func (user *User) CountryCodeStr() string {
	if user.CountryCode == 0 {
		return ""
	}

	return strconv.Itoa(user.CountryCode)
}

// AuthyIDStr returns the authy id as a string.
func (user *User) AuthyIDStr() string {
	if user.AuthyID == "" {
		return "<not set>"
	}

	return user.AuthyID
}

// ToMap converts the user to a map
func (user *User) ToMap() DatabaseData {
	return DatabaseData{
		"Username":    user.Username,
		"AuthyID":     user.AuthyID,
		"Email":       user.Email,
		"PublicKeys":  user.PublicKeys,
		"CountryCode": user.CountryCode,
		"PhoneNumber": user.PhoneNumber,
	}
}

// FromMap loads the user using a map.
func (user *User) FromMap(data DatabaseData) {
	if value := data["Username"]; value != nil {
		user.Username = value.(string)
	}
	if value := data["AuthyID"]; value != nil {
		user.AuthyID = value.(string)
	}
	if value := data["Email"]; value != nil {
		user.Email = value.(string)
	}
	if value := data["CountryCode"]; value != nil {
		user.CountryCode = value.(int)
	}
	if value := data["PhoneNumber"]; value != nil {
		user.PhoneNumber = value.(string)
	}
	if value := data["PublicKeys"]; value != nil {
		user.PublicKeys = value.([]string)
	}
}

// Register register the user on Authy
func (user *User) Register() error {
	if len(user.PhoneNumber) == 0 || user.CountryCode == 0 {
		return errors.New("Invalid phone number.")
	}

	config, err := LoadConfig()
	if err != nil {
		return err
	}

	api := authy.NewAuthyAPI(config.APIKey)
	api.BaseURL = "https://api.authy.com"

	authyUser, err := api.RegisterUser(user.Email, user.CountryCode, user.PhoneNumber, url.Values{})
	if err != nil {
		return err
	}

	user.AuthyID = authyUser.ID
	return nil
}

// ValueForColumn returns the value for the given column name.
func (user *User) ValueForColumn(columnName string) string {
	switch columnName {
	case "ID":
		{
			return user.AuthyIDStr()
		}
	case "Username":
		{
			return user.Username
		}
	case "Email":
		{
			return user.Email
		}
	case "Phone Number":
		{
			return fmt.Sprintf("+%s %s", user.CountryCodeStr(), user.PhoneNumber)
		}
	case "Configured":
		{
			if user.IsConfigured() {
				return "YES"
			}
			return "NO"
		}
	case "Protected":
		{
			if user.IsProtected() {
				return "YES"
			}
			return "NO"
		}
	}
	return "<unknown>"
}

// IsConfigured returns true if the user is fully configured
func (user *User) IsConfigured() bool {
	if user.AuthyID != "" && len(user.PublicKeys) > 0 {
		return true
	}
	return false
}

// IsProtected returns true if the user is fully configured
func (user *User) IsProtected() bool {
	manager := NewAuthorizedKeysManager()
	return manager.Contains(fmt.Sprintf("authy-shell %d", user.AuthyID))
}
<commit_msg>Trim some old code from ssh/user.go<commit_after>package ssh

import (
	"errors"
	"fmt"
	"github.com/dcu/go-authy"
	"net/url"
	"strconv"
)

// User is a struct contains the user's info.
type User struct {
	Username    string
	PublicKeys  []string
	Email       string
	CountryCode int
	PhoneNumber string
	AuthyID     string
}

// NewUser returns a new instance of User
func NewUser(username string) *User {
	user := &User{
		Username: username,
	}
	return user
}

// LoadUser loads user data from the Authy API
func (user *User) LoadUser(authyId string) error {
	if len(authyId) == 0 {
		return errors.New("Invalid Authy ID.")
	}

	config, err := LoadConfig()
	if err != nil {
		return err
	}

	api := authy.NewAuthyAPI(config.APIKey)
	api.BaseURL = "https://api.authy.com"

	authyUser, err := api.UserStatus(authyId, url.Values{})
	if err != nil {
		return err
	}

	user.AuthyID = authyUser.ID
	user.CountryCode = authyUser.StatusData.Country
	user.PhoneNumber = authyUser.StatusData.PhoneNumber

	return nil
}

// Save saves the user
func Save() bool {
	return false
}

// CountryCodeStr returns the country code as a string.
func (user *User) CountryCodeStr() string {
	if user.CountryCode == 0 {
		return ""
	}

	return strconv.Itoa(user.CountryCode)
}

// AuthyIDStr returns the authy id as a string.
func (user *User) AuthyIDStr() string {
	if user.AuthyID == "" {
		return "<not set>"
	}

	return user.AuthyID
}

// Register register the user on Authy
func (user *User) Register() error {
	if len(user.PhoneNumber) == 0 || user.CountryCode == 0 {
		return errors.New("Invalid phone number.")
	}

	config, err := LoadConfig()
	if err != nil {
		return err
	}

	api := authy.NewAuthyAPI(config.APIKey)
	api.BaseURL = "https://api.authy.com"

	authyUser, err := api.RegisterUser(user.Email, user.CountryCode, user.PhoneNumber, url.Values{})
	if err != nil {
		return err
	}

	user.AuthyID = authyUser.ID
	return nil
}

// ValueForColumn returns the value for the given column name.
func (user *User) ValueForColumn(columnName string) string {
	switch columnName {
	case "ID":
		{
			return user.AuthyIDStr()
		}
	case "Username":
		{
			return user.Username
		}
	case "Email":
		{
			return user.Email
		}
	case "Phone Number":
		{
			return fmt.Sprintf("+%s %s", user.CountryCodeStr(), user.PhoneNumber)
		}
	case "Configured":
		{
			if user.IsConfigured() {
				return "YES"
			}
			return "NO"
		}
	case "Protected":
		{
			if user.IsProtected() {
				return "YES"
			}
			return "NO"
		}
	}
	return "<unknown>"
}

// IsConfigured returns true if the user is fully configured
func (user *User) IsConfigured() bool {
	if user.AuthyID != "" && len(user.PublicKeys) > 0 {
		return true
	}
	return false
}

// IsProtected returns true if the user is fully configured
func (user *User) IsProtected() bool {
	manager := NewAuthorizedKeysManager()
	return manager.Contains(fmt.Sprintf("authy-shell %d", user.AuthyID))
}
<|endoftext|>


<commit_before>// +build linux freebsd netbsd openbsd

package tarheader

import (
	"archive/tar"
	"os"
	"syscall"

	"github.com/appc/spec/pkg/device"
)

func init() {
	populateHeaderStat = append(populateHeaderStat, populateHeaderUnix)
}

func populateHeaderUnix(h *tar.Header, fi os.FileInfo, seen map[uint64]string) {
	st, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return
	}
	h.Uid = int(st.Uid)
	h.Gid = int(st.Gid)
	if st.Mode&syscall.S_IFMT == syscall.S_IFBLK || st.Mode&syscall.S_IFMT == syscall.S_IFCHR {
		h.Devminor = int64(device.Minor(st.Rdev))
		h.Devmajor = int64(device.Major(st.Rdev))
	}
	// If we have already seen this inode, generate a hardlink
	p, ok := seen[uint64(st.Ino)]
	if ok {
		h.Linkname = p
		h.Typeflag = tar.TypeLink
	} else {
		seen[uint64(st.Ino)] = h.Name
	}
}
<commit_msg>appc/spec: Patch in-place for appc/spec#419<commit_after>// +build linux freebsd netbsd openbsd

package tarheader

import (
	"archive/tar"
	"os"
	"syscall"

	"github.com/appc/spec/pkg/device"
)

func init() {
	populateHeaderStat = append(populateHeaderStat, populateHeaderUnix)
}

func populateHeaderUnix(h *tar.Header, fi os.FileInfo, seen map[uint64]string) {
	st, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return
	}
	h.Uid = int(st.Uid)
	h.Gid = int(st.Gid)
	if st.Mode&syscall.S_IFMT == syscall.S_IFBLK || st.Mode&syscall.S_IFMT == syscall.S_IFCHR {
		h.Devminor = int64(device.Minor(uint64(st.Rdev)))
		h.Devmajor = int64(device.Major(uint64(st.Rdev)))
	}
	// If we have already seen this inode, generate a hardlink
	p, ok := seen[uint64(st.Ino)]
	if ok {
		h.Linkname = p
		h.Typeflag = tar.TypeLink
	} else {
		seen[uint64(st.Ino)] = h.Name
	}
}
<|endoftext|>


<commit_before>package arn

// ClientErrorReport saves JavaScript errors that happen in web clients like browsers.
type ClientErrorReport struct {
	ID           string `json:"id"`
	Message      string `json:"message"`
	Stack        string `json:"stack"`
	FileName     string `json:"fileName"`
	LineNumber   int    `json:"lineNumber"`
	ColumnNumber int    `json:"columnNumber"`

	HasCreator
}
<commit_msg>Added iteration for client error reports<commit_after>package arn

import "github.com/aerogo/nano"

// ClientErrorReport saves JavaScript errors that happen in web clients like browsers.
type ClientErrorReport struct {
	ID           string `json:"id"`
	Message      string `json:"message"`
	Stack        string `json:"stack"`
	FileName     string `json:"fileName"`
	LineNumber   int    `json:"lineNumber"`
	ColumnNumber int    `json:"columnNumber"`

	HasCreator
}

// StreamClientErrorReports returns a stream of all characters.
func StreamClientErrorReports() chan *ClientErrorReport {
	channel := make(chan *ClientErrorReport, nano.ChannelBufferSize)

	go func() {
		for obj := range DB.All("ClientErrorReport") {
			channel <- obj.(*ClientErrorReport)
		}

		close(channel)
	}()

	return channel
}

// AllClientErrorReports returns a slice of all characters.
func AllClientErrorReports() []*ClientErrorReport {
	var all []*ClientErrorReport

	stream := StreamClientErrorReports()

	for obj := range stream {
		all = append(all, obj)
	}

	return all
}
<|endoftext|>


<commit_before>package v7action

import (
	"context"
	"errors"
	"log"
	"strings"
	"time"

	"code.cloudfoundry.org/go-loggregator/rpc/loggregator_v2"
	logcache "code.cloudfoundry.org/log-cache/pkg/client"
	"code.cloudfoundry.org/log-cache/pkg/rpc/logcache_v1"
	"github.com/sirupsen/logrus"

	"code.cloudfoundry.org/cli/actor/loggingaction"
	"github.com/SermoDigital/jose/jws"
)

const StagingLog = "STG"

var flushInterval = 300 * time.Millisecond

type LogMessage struct {
	message        string
	messageType    string
	timestamp      time.Time
	sourceType     string
	sourceInstance string
}

func (log LogMessage) Message() string {
	return log.message
}

func (log LogMessage) Type() string {
	return log.messageType
}

func (log LogMessage) Staging() bool {
	return log.sourceType == StagingLog
}

func (log LogMessage) Timestamp() time.Time {
	return log.timestamp
}

func (log LogMessage) SourceType() string {
	return log.sourceType
}

func (log LogMessage) SourceInstance() string {
	return log.sourceInstance
}

func NewLogMessage(message string, messageType string, timestamp time.Time, sourceType string, sourceInstance string) *LogMessage {
	return &LogMessage{
		message:        message,
		messageType:    messageType,
		timestamp:      timestamp,
		sourceType:     sourceType,
		sourceInstance: sourceInstance,
	}
}

type LogMessages []*LogMessage

func (lm LogMessages) Len() int { return len(lm) }

func (lm LogMessages) Less(i, j int) bool {
	return lm[i].timestamp.Before(lm[j].timestamp)
}

func (lm LogMessages) Swap(i, j int) {
	lm[i], lm[j] = lm[j], lm[i]
}

type channelWriter struct {
	errChannel chan error
}

func (c channelWriter) Write(bytes []byte) (n int, err error) {
	c.errChannel <- errors.New(strings.Trim(string(bytes), "\n"))

	return len(bytes), nil
}

func GetStreamingLogs(appGUID string, client LogCacheClient) (<-chan LogMessage, <-chan error, context.CancelFunc) {
	logrus.Info("Start Tailing Logs")

	outgoingLogStream := make(chan LogMessage, 1000)
	outgoingErrStream := make(chan error, 1000)
	ctx, cancelFunc := context.WithCancel(context.Background())
	go func() {
		defer close(outgoingLogStream)
		defer close(outgoingErrStream)

		logcache.Walk(
			ctx,
			appGUID,
			logcache.Visitor(func(envelopes []*loggregator_v2.Envelope) bool {
				logMessages := convertEnvelopesToLogMessages(envelopes)
				for _, logMessage := range logMessages {
					select {
					case <-ctx.Done():
						return false
					default:
						outgoingLogStream <- *logMessage
					}
				}

				return true
			}),
			client.Read,
			logcache.WithWalkStartTime(time.Now().Add(-5*time.Second)),
			logcache.WithWalkEnvelopeTypes(logcache_v1.EnvelopeType_LOG),
			logcache.WithWalkBackoff(logcache.NewAlwaysRetryBackoff(250*time.Millisecond)),
			logcache.WithWalkLogger(log.New(channelWriter{
				errChannel: outgoingErrStream,
			}, "", 0)),
		)
	}()

	return outgoingLogStream, outgoingErrStream, cancelFunc
}

func convertEnvelopesToLogMessages(envelopes []*loggregator_v2.Envelope) []*LogMessage {
	var logMessages []*LogMessage
	for _, envelope := range envelopes {
		logEnvelope, ok := envelope.GetMessage().(*loggregator_v2.Envelope_Log)
		if !ok {
			continue
		}
		log := logEnvelope.Log

		logMessages = append(logMessages, NewLogMessage(
			string(log.Payload),
			loggregator_v2.Log_Type_name[int32(log.Type)],
			time.Unix(0, envelope.GetTimestamp()),
			envelope.GetTags()["source_type"],
			envelope.GetInstanceId(),
		))
	}
	return logMessages
}

func (actor Actor) GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client LogCacheClient) (<-chan LogMessage, <-chan error, context.CancelFunc, Warnings, error) {
	app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
	if err != nil {
		return nil, nil, nil, allWarnings, err
	}

	messages, logErrs, cancelFunc := GetStreamingLogs(app.GUID, client)

	return messages, logErrs, cancelFunc, allWarnings, err
}

func (actor Actor) GetRecentLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client LogCacheClient) ([]LogMessage, Warnings, error) {
	app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
	if err != nil {
		return nil, allWarnings, err
	}

	logCacheMessages, err := loggingaction.GetRecentLogs(app.GUID, client)
	if err != nil {
		return nil, allWarnings, err
	}

	//TODO: Messages need sorting for most recent?
	// logCacheMessages = client.SortRecent(logCacheMessages)

	var logMessages []LogMessage

	for _, message := range logCacheMessages {
		logMessages = append(logMessages, LogMessage{
			message:        message.Message,
			messageType:    message.MessageType,
			timestamp:      message.Timestamp, // time.Unix(0, message.Timestamp),
			sourceType:     message.SourceType,
			sourceInstance: message.SourceInstance,
		})
	}

	return logMessages, allWarnings, nil
}

func (actor Actor) ScheduleTokenRefresh() (chan bool, error) {
	accessTokenString, err := actor.RefreshAccessToken()
	if err != nil {
		return nil, err
	}
	accessTokenString = strings.TrimPrefix(accessTokenString, "bearer ")
	token, err := jws.ParseJWT([]byte(accessTokenString))
	if err != nil {
		return nil, err
	}

	var expiresIn time.Duration
	expiration, ok := token.Claims().Expiration()
	if ok {
		expiresIn = time.Until(expiration)

		// When we refresh exactly every EXPIRY_DURATION nanoseconds usually the auth token
		// ends up expiring on the log-cache client. Better to refresh a little more often
		// to avoid log outage
		expiresIn = expiresIn * 9 / 10
	} else {
		return nil, errors.New("Failed to get an expiry time from the current access token")
	}
	quitNowChannel := make(chan bool, 1)

	go func() {
		ticker := time.NewTicker(expiresIn)
		defer ticker.Stop()
		for {
			select {
			case _, _ = <-ticker.C:
				_, err := actor.RefreshAccessToken()
				if err != nil {
					panic(err)
				}
			case _, _ = <-quitNowChannel:
				return
			}
		}
	}()

	return quitNowChannel, nil
}
<commit_msg>cf logs - fix lint errors<commit_after>package v7action

import (
	"context"
	"errors"
	"log"
	"strings"
	"time"

	"code.cloudfoundry.org/go-loggregator/rpc/loggregator_v2"
	logcache "code.cloudfoundry.org/log-cache/pkg/client"
	"code.cloudfoundry.org/log-cache/pkg/rpc/logcache_v1"
	"github.com/sirupsen/logrus"

	"code.cloudfoundry.org/cli/actor/loggingaction"
	"github.com/SermoDigital/jose/jws"
)

const StagingLog = "STG"

type LogMessage struct {
	message        string
	messageType    string
	timestamp      time.Time
	sourceType     string
	sourceInstance string
}

func (log LogMessage) Message() string {
	return log.message
}

func (log LogMessage) Type() string {
	return log.messageType
}

func (log LogMessage) Staging() bool {
	return log.sourceType == StagingLog
}

func (log LogMessage) Timestamp() time.Time {
	return log.timestamp
}

func (log LogMessage) SourceType() string {
	return log.sourceType
}

func (log LogMessage) SourceInstance() string {
	return log.sourceInstance
}

func NewLogMessage(message string, messageType string, timestamp time.Time, sourceType string, sourceInstance string) *LogMessage {
	return &LogMessage{
		message:        message,
		messageType:    messageType,
		timestamp:      timestamp,
		sourceType:     sourceType,
		sourceInstance: sourceInstance,
	}
}

type LogMessages []*LogMessage

func (lm LogMessages) Len() int { return len(lm) }

func (lm LogMessages) Less(i, j int) bool {
	return lm[i].timestamp.Before(lm[j].timestamp)
}

func (lm LogMessages) Swap(i, j int) {
	lm[i], lm[j] = lm[j], lm[i]
}

type channelWriter struct {
	errChannel chan error
}

func (c channelWriter) Write(bytes []byte) (n int, err error) {
	c.errChannel <- errors.New(strings.Trim(string(bytes), "\n"))

	return len(bytes), nil
}

func GetStreamingLogs(appGUID string, client LogCacheClient) (<-chan LogMessage, <-chan error, context.CancelFunc) {
	logrus.Info("Start Tailing Logs")

	outgoingLogStream := make(chan LogMessage, 1000)
	outgoingErrStream := make(chan error, 1000)
	ctx, cancelFunc := context.WithCancel(context.Background())
	go func() {
		defer close(outgoingLogStream)
		defer close(outgoingErrStream)

		logcache.Walk(
			ctx,
			appGUID,
			logcache.Visitor(func(envelopes []*loggregator_v2.Envelope) bool {
				logMessages := convertEnvelopesToLogMessages(envelopes)
				for _, logMessage := range logMessages {
					select {
					case <-ctx.Done():
						return false
					default:
						outgoingLogStream <- *logMessage
					}
				}

				return true
			}),
			client.Read,
			logcache.WithWalkStartTime(time.Now().Add(-5*time.Second)),
			logcache.WithWalkEnvelopeTypes(logcache_v1.EnvelopeType_LOG),
			logcache.WithWalkBackoff(logcache.NewAlwaysRetryBackoff(250*time.Millisecond)),
			logcache.WithWalkLogger(log.New(channelWriter{
				errChannel: outgoingErrStream,
			}, "", 0)),
		)
	}()

	return outgoingLogStream, outgoingErrStream, cancelFunc
}

func convertEnvelopesToLogMessages(envelopes []*loggregator_v2.Envelope) []*LogMessage {
	var logMessages []*LogMessage
	for _, envelope := range envelopes {
		logEnvelope, ok := envelope.GetMessage().(*loggregator_v2.Envelope_Log)
		if !ok {
			continue
		}
		log := logEnvelope.Log

		logMessages = append(logMessages, NewLogMessage(
			string(log.Payload),
			loggregator_v2.Log_Type_name[int32(log.Type)],
			time.Unix(0, envelope.GetTimestamp()),
			envelope.GetTags()["source_type"],
			envelope.GetInstanceId(),
		))
	}
	return logMessages
}

func (actor Actor) GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client LogCacheClient) (<-chan LogMessage, <-chan error, context.CancelFunc, Warnings, error) {
	app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
	if err != nil {
		return nil, nil, nil, allWarnings, err
	}

	messages, logErrs, cancelFunc := GetStreamingLogs(app.GUID, client)

	return messages, logErrs, cancelFunc, allWarnings, err
}

func (actor Actor) GetRecentLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client LogCacheClient) ([]LogMessage, Warnings, error) {
	app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
	if err != nil {
		return nil, allWarnings, err
	}

	logCacheMessages, err := loggingaction.GetRecentLogs(app.GUID, client)
	if err != nil {
		return nil, allWarnings, err
	}

	//TODO: Messages need sorting for most recent?
	// logCacheMessages = client.SortRecent(logCacheMessages)

	var logMessages []LogMessage

	for _, message := range logCacheMessages {
		logMessages = append(logMessages, LogMessage{
			message:        message.Message,
			messageType:    message.MessageType,
			timestamp:      message.Timestamp, // time.Unix(0, message.Timestamp),
			sourceType:     message.SourceType,
			sourceInstance: message.SourceInstance,
		})
	}

	return logMessages, allWarnings, nil
}

func (actor Actor) ScheduleTokenRefresh() (chan bool, error) {
	accessTokenString, err := actor.RefreshAccessToken()
	if err != nil {
		return nil, err
	}
	accessTokenString = strings.TrimPrefix(accessTokenString, "bearer ")
	token, err := jws.ParseJWT([]byte(accessTokenString))
	if err != nil {
		return nil, err
	}

	var expiresIn time.Duration
	expiration, ok := token.Claims().Expiration()
	if ok {
		expiresIn = time.Until(expiration)

		// When we refresh exactly every EXPIRY_DURATION nanoseconds usually the auth token
		// ends up expiring on the log-cache client. Better to refresh a little more often
		// to avoid log outage
		expiresIn = expiresIn * 9 / 10
	} else {
		return nil, errors.New("Failed to get an expiry time from the current access token")
	}
	quitNowChannel := make(chan bool, 1)

	go func() {
		ticker := time.NewTicker(expiresIn)
		defer ticker.Stop()
		for {
			select {
			case _ = <-ticker.C:
				_, err := actor.RefreshAccessToken()
				if err != nil {
					panic(err)
				}
			case _ = <-quitNowChannel:
				return
			}
		}
	}()

	return quitNowChannel, nil
}
<|endoftext|>


<commit_before>// +build !windows
// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 stats

import (
	"fmt"

	"github.com/cihub/seelog"
	"github.com/docker/docker/api/types"
)

// dockerStatsToContainerStats returns a new object of the ContainerStats object from docker stats.
func dockerStatsToContainerStats(dockerStats *types.StatsJSON) (*ContainerStats, error) {
	// The length of PercpuUsage represents the number of cores in an instance.
	if len(dockerStats.CPUStats.CPUUsage.PercpuUsage) == 0 || numCores == uint64(0) {
		seelog.Debug("Invalid container statistics reported, no cpu core usage reported")
		return nil, fmt.Errorf("Invalid container statistics reported, no cpu core usage reported")
	}

	cpuUsage := dockerStats.CPUStats.CPUUsage.TotalUsage / numCores
	memoryUsage := dockerStats.MemoryStats.Usage - dockerStats.MemoryStats.Stats["cache"]
	storageReadBytes, storageWriteBytes := getStorageStats(dockerStats)
	networkStats := getNetworkStats(dockerStats)
	return &ContainerStats{
		cpuUsage:          cpuUsage,
		memoryUsage:       memoryUsage,
		storageReadBytes:  storageReadBytes,
		storageWriteBytes: storageWriteBytes,
		networkStats:      networkStats,
		timestamp:         dockerStats.Read,
	}, nil
}

func getStorageStats(dockerStats *types.StatsJSON) (uint64, uint64) {
	// initialize block io and loop over stats to aggregate
	storageReadBytes := uint64(0)
	storageWriteBytes := uint64(0)
	for _, blockStat := range dockerStats.BlkioStats.IoServiceBytesRecursive {
		switch op := blockStat.Op; op {
		case "Read":
			storageReadBytes += blockStat.Value
		case "Write":
			storageWriteBytes += blockStat.Value
		default:
			//ignoring "Async", "Total", "Sum", etc
			continue
		}
	}
	return storageReadBytes, storageWriteBytes
}
<commit_msg>add nil check for iobytes<commit_after>// +build !windows
// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 stats

import (
	"fmt"

	"github.com/cihub/seelog"
	"github.com/docker/docker/api/types"
)

// dockerStatsToContainerStats returns a new object of the ContainerStats object from docker stats.
func dockerStatsToContainerStats(dockerStats *types.StatsJSON) (*ContainerStats, error) {
	// The length of PercpuUsage represents the number of cores in an instance.
	if len(dockerStats.CPUStats.CPUUsage.PercpuUsage) == 0 || numCores == uint64(0) {
		seelog.Debug("Invalid container statistics reported, no cpu core usage reported")
		return nil, fmt.Errorf("Invalid container statistics reported, no cpu core usage reported")
	}

	cpuUsage := dockerStats.CPUStats.CPUUsage.TotalUsage / numCores
	memoryUsage := dockerStats.MemoryStats.Usage - dockerStats.MemoryStats.Stats["cache"]
	storageReadBytes, storageWriteBytes := getStorageStats(dockerStats)
	networkStats := getNetworkStats(dockerStats)
	return &ContainerStats{
		cpuUsage:          cpuUsage,
		memoryUsage:       memoryUsage,
		storageReadBytes:  storageReadBytes,
		storageWriteBytes: storageWriteBytes,
		networkStats:      networkStats,
		timestamp:         dockerStats.Read,
	}, nil
}

func getStorageStats(dockerStats *types.StatsJSON) (uint64, uint64) {
	// initialize block io and loop over stats to aggregate
	if dockerStats.BlkioStats.IoServiceBytesRecursive == nil {
		seelog.Debug("Storage stats not reported for container")
		return uint64(0), uint64(0)
	}
	storageReadBytes := uint64(0)
	storageWriteBytes := uint64(0)
	for _, blockStat := range dockerStats.BlkioStats.IoServiceBytesRecursive {
		switch op := blockStat.Op; op {
		case "Read":
			storageReadBytes += blockStat.Value
		case "Write":
			storageWriteBytes += blockStat.Value
		default:
			//ignoring "Async", "Total", "Sum", etc
			continue
		}
	}
	return storageReadBytes, storageWriteBytes
}
<|endoftext|>


<commit_before>// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package domain

import (
	"sync/atomic"
	"time"

	"github.com/pingcap/tidb/metrics"
)

// SchemaChecker is used for checking schema-validity.
type SchemaChecker struct {
	SchemaValidator
	schemaVer       int64
	relatedTableIDs []int64
}

var (
	// SchemaOutOfDateRetryInterval is the backoff time before retrying.
	SchemaOutOfDateRetryInterval = int64(500 * time.Millisecond)
	// SchemaOutOfDateRetryTimes is the max retry count when the schema is out of date.
	SchemaOutOfDateRetryTimes = int32(10)
)

// NewSchemaChecker creates a new schema checker.
func NewSchemaChecker(do *Domain, schemaVer int64, relatedTableIDs []int64) *SchemaChecker {
	return &SchemaChecker{
		SchemaValidator: do.SchemaValidator,
		schemaVer:       schemaVer,
		relatedTableIDs: relatedTableIDs,
	}
}

// Check checks the validity of the schema version.
func (s *SchemaChecker) Check(txnTS uint64) error {
	schemaOutOfDateRetryInterval := atomic.LoadInt64(&SchemaOutOfDateRetryInterval)
	schemaOutOfDateRetryTimes := int(atomic.LoadInt32(&SchemaOutOfDateRetryTimes))
	for i := 0; i < schemaOutOfDateRetryTimes; i++ {
		result := s.SchemaValidator.Check(txnTS, s.schemaVer, s.relatedTableIDs)
		switch result {
		case ResultSucc:
			return nil
		case ResultFail:
			metrics.SchemaLeaseErrorCounter.WithLabelValues("changed").Inc()
			return ErrInfoSchemaChanged
		case ResultUnknown:
			metrics.SchemaLeaseErrorCounter.WithLabelValues("outdated").Inc()
			time.Sleep(time.Duration(schemaOutOfDateRetryInterval))
		}

	}
	return ErrInfoSchemaExpired
}
<commit_msg>domain: update the schema check outdate metric (#11134)<commit_after>// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package domain

import (
	"sync/atomic"
	"time"

	"github.com/pingcap/tidb/metrics"
)

// SchemaChecker is used for checking schema-validity.
type SchemaChecker struct {
	SchemaValidator
	schemaVer       int64
	relatedTableIDs []int64
}

var (
	// SchemaOutOfDateRetryInterval is the backoff time before retrying.
	SchemaOutOfDateRetryInterval = int64(500 * time.Millisecond)
	// SchemaOutOfDateRetryTimes is the max retry count when the schema is out of date.
	SchemaOutOfDateRetryTimes = int32(10)
)

// NewSchemaChecker creates a new schema checker.
func NewSchemaChecker(do *Domain, schemaVer int64, relatedTableIDs []int64) *SchemaChecker {
	return &SchemaChecker{
		SchemaValidator: do.SchemaValidator,
		schemaVer:       schemaVer,
		relatedTableIDs: relatedTableIDs,
	}
}

// Check checks the validity of the schema version.
func (s *SchemaChecker) Check(txnTS uint64) error {
	schemaOutOfDateRetryInterval := atomic.LoadInt64(&SchemaOutOfDateRetryInterval)
	schemaOutOfDateRetryTimes := int(atomic.LoadInt32(&SchemaOutOfDateRetryTimes))
	for i := 0; i < schemaOutOfDateRetryTimes; i++ {
		result := s.SchemaValidator.Check(txnTS, s.schemaVer, s.relatedTableIDs)
		switch result {
		case ResultSucc:
			return nil
		case ResultFail:
			metrics.SchemaLeaseErrorCounter.WithLabelValues("changed").Inc()
			return ErrInfoSchemaChanged
		case ResultUnknown:
			time.Sleep(time.Duration(schemaOutOfDateRetryInterval))
		}

	}
	metrics.SchemaLeaseErrorCounter.WithLabelValues("outdated").Inc()
	return ErrInfoSchemaExpired
}
<|endoftext|>


<commit_before>package downloader

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"
)

const (
	// AudistoAPIDomain the domain name endpoint for Audisto API
	AudistoAPIDomain = "api.audisto.com"

	// AudistoAPIEndpoint URL enpoint for Audisto API, put "" or "/" string if the endpoint is at the root domain
	AudistoAPIEndpoint = "/crawls/"

	// AudistoAPIVersion the version of Audisto API version this downloader will talk to
	AudistoAPIVersion = "2.0"

	// EndpointSchema http or https, this probably wont change, hence it is set here
	EndpointSchema = "https"

	// DefaultRequestMethod used when http request method is not explicitly set
	DefaultRequestMethod = "GET"

	// DefaultOutputFormat the default formatting or file extension for the response we get from Audisto API if not expilictly set
	DefaultOutputFormat = "tsv"

	// DefaultChunkSize the default chunk size for interacting with Audisto API if NOT expilicty set
	// This should not affect the way throttling works
	DefaultChunkSize = 10000
)

// AudistoAPIClient a struct holding all information requred to construct a URL with query params for Audisto API
type AudistoAPIClient struct {

	// request path / DSN
	BasePath string
	Username string
	Password string
	Mode     string
	CrawlID  uint64

	// request query params
	Deep        bool
	Filter      string
	Order       string
	Output      string
	ChunkNumber uint64
	ChunkSize   uint64

	// meta
	requestMethod string
}

// IsValid check if the struct info look good. this does not do any remote request.
func (api *AudistoAPIClient) IsValid() error {

	if api.Username == "" || api.Password == "" || api.CrawlID == 0 {
		return fmt.Errorf("username, password or crawl should NOT be empty")
	}

	if api.Mode != "" && api.Mode != "pages" && api.Mode != "links" {
		return fmt.Errorf("mode has to be 'links' or 'pages'")
	}

	return nil
}

// GetAPIEndpoint constructs the Audisto API endpoint without the query params nor the dsn part.
func (api *AudistoAPIClient) GetAPIEndpoint() string {
	endpoint := strings.Trim(AudistoAPIEndpoint, "/")
	urlParts := []string{AudistoAPIDomain, AudistoAPIVersion, endpoint}
	return strings.Join(urlParts, "/")
}

// GetBaseURL construct the base url for quering Audisto API in the form of:
// username:password@api.audisto.com
func (api *AudistoAPIClient) GetBaseURL() string {
	return fmt.Sprintf(
		"%s://%s:%s@%s",
		EndpointSchema, api.Username, api.Password, api.GetAPIEndpoint())
}

// GetURLPath returns the full url for interacting with Audisto API, WITHOUT query params
// e.g. username:password@api.audisto.com/crawls/pages|links
func (api *AudistoAPIClient) GetURLPath() string {
	return fmt.Sprintf("%s/%v/%s", api.GetBaseURL(), api.CrawlID, api.Mode)
}

// GetRelativePath return the relative path to the api domain name
func (api *AudistoAPIClient) GetRelativePath() string {
	endpoint := strings.Trim(AudistoAPIEndpoint, "/")
	return fmt.Sprintf(
		"/%s/%s/%v/%s",
		AudistoAPIVersion, endpoint, api.CrawlID, api.Mode)
}

// GetQueryParams use net/url package to construct query params
func (api *AudistoAPIClient) GetQueryParams() url.Values {
	urlQueryParams := url.Values{}

	if api.Deep {
		urlQueryParams.Add("deep", "1")
	} else {
		urlQueryParams.Add("deep", "0")
	}

	if api.Filter != "" {
		urlQueryParams.Add("filter", api.Filter)
	}

	if api.Order != "" {
		urlQueryParams.Add("order", api.Order)
	}

	if api.Output == "" {
		urlQueryParams.Add("output", DefaultOutputFormat)
	} else {
		urlQueryParams.Add("output", api.Output)
	}

	urlQueryParams.Add("chunk", strconv.FormatUint(api.ChunkNumber, 10))
	urlQueryParams.Add("chunk_size", strconv.FormatUint(api.ChunkSize, 10))
	return urlQueryParams
}

// GetFullQueryURL returns the full url for interacting with Audisto API, INCLUDING query params
func (api *AudistoAPIClient) GetFullQueryURL() string {
	return api.GetQueryParams().Encode()
}

// SetChunkSize set AudistoAPI.ChunkSize to a new size
func (api *AudistoAPIClient) SetChunkSize(size uint64) {
	if size == 0 {
		api.ChunkSize = DefaultChunkSize
	} else {
		api.ChunkSize = size
	}
}

// SetNextChunk set AudistoAPI.ChunkNumber to the next chunk number
func (api *AudistoAPIClient) SetNextChunk(number uint64) {
	api.ChunkNumber = number
}

// GetRequestMethod returns the HTTP request method, GET (by default)
func (api *AudistoAPIClient) GetRequestMethod() string {
	if api.requestMethod == "" {
		return DefaultRequestMethod
	}
	return api.requestMethod
}

// SetRequestMethod sets the HTTP request method for interacting with Audisto API
// Allowed method: GET, POST, PATCH, DELETE
func (api *AudistoAPIClient) SetRequestMethod(method string) error {
	method = strings.ToUpper(method)

	if method != "GET" && method != "POST" && method != "PATCH" && method != "DELETE" {
		return fmt.Errorf("Method not supported: %s", method)
	}
	api.requestMethod = method
	return nil
}

// GetRequestURL returns a validated instance of url.URL, and an error if the validation fails
func (api *AudistoAPIClient) GetRequestURL() (*url.URL, error) {
	domain := api.GetBaseURL()
	requestURL, err := url.Parse(domain)
	if err != nil {
		return requestURL, err
	}
	requestURL.Path = api.GetRelativePath()
	requestURL.RawQuery = api.GetQueryParams().Encode()
	return requestURL, nil
}
<commit_msg>rename SetNextChunk to SetNextChunkNumber for AudistoAPIClient<commit_after>package downloader

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"
)

const (
	// AudistoAPIDomain the domain name endpoint for Audisto API
	AudistoAPIDomain = "api.audisto.com"

	// AudistoAPIEndpoint URL enpoint for Audisto API, put "" or "/" string if the endpoint is at the root domain
	AudistoAPIEndpoint = "/crawls/"

	// AudistoAPIVersion the version of Audisto API version this downloader will talk to
	AudistoAPIVersion = "2.0"

	// EndpointSchema http or https, this probably wont change, hence it is set here
	EndpointSchema = "https"

	// DefaultRequestMethod used when http request method is not explicitly set
	DefaultRequestMethod = "GET"

	// DefaultOutputFormat the default formatting or file extension for the response we get from Audisto API if not expilictly set
	DefaultOutputFormat = "tsv"

	// DefaultChunkSize the default chunk size for interacting with Audisto API if NOT expilicty set
	// This should not affect the way throttling works
	DefaultChunkSize = 10000
)

// AudistoAPIClient a struct holding all information requred to construct a URL with query params for Audisto API
type AudistoAPIClient struct {

	// request path / DSN
	BasePath string
	Username string
	Password string
	Mode     string
	CrawlID  uint64

	// request query params
	Deep        bool
	Filter      string
	Order       string
	Output      string
	ChunkNumber uint64
	ChunkSize   uint64

	// meta
	requestMethod string
}

// IsValid check if the struct info look good. this does not do any remote request.
func (api *AudistoAPIClient) IsValid() error {

	if api.Username == "" || api.Password == "" || api.CrawlID == 0 {
		return fmt.Errorf("username, password or crawl should NOT be empty")
	}

	if api.Mode != "" && api.Mode != "pages" && api.Mode != "links" {
		return fmt.Errorf("mode has to be 'links' or 'pages'")
	}

	return nil
}

// GetAPIEndpoint constructs the Audisto API endpoint without the query params nor the dsn part.
func (api *AudistoAPIClient) GetAPIEndpoint() string {
	endpoint := strings.Trim(AudistoAPIEndpoint, "/")
	urlParts := []string{AudistoAPIDomain, AudistoAPIVersion, endpoint}
	return strings.Join(urlParts, "/")
}

// GetBaseURL construct the base url for quering Audisto API in the form of:
// username:password@api.audisto.com
func (api *AudistoAPIClient) GetBaseURL() string {
	return fmt.Sprintf(
		"%s://%s:%s@%s",
		EndpointSchema, api.Username, api.Password, api.GetAPIEndpoint())
}

// GetURLPath returns the full url for interacting with Audisto API, WITHOUT query params
// e.g. username:password@api.audisto.com/crawls/pages|links
func (api *AudistoAPIClient) GetURLPath() string {
	return fmt.Sprintf("%s/%v/%s", api.GetBaseURL(), api.CrawlID, api.Mode)
}

// GetRelativePath return the relative path to the api domain name
func (api *AudistoAPIClient) GetRelativePath() string {
	endpoint := strings.Trim(AudistoAPIEndpoint, "/")
	return fmt.Sprintf(
		"/%s/%s/%v/%s",
		AudistoAPIVersion, endpoint, api.CrawlID, api.Mode)
}

// GetQueryParams use net/url package to construct query params
func (api *AudistoAPIClient) GetQueryParams() url.Values {
	urlQueryParams := url.Values{}

	if api.Deep {
		urlQueryParams.Add("deep", "1")
	} else {
		urlQueryParams.Add("deep", "0")
	}

	if api.Filter != "" {
		urlQueryParams.Add("filter", api.Filter)
	}

	if api.Order != "" {
		urlQueryParams.Add("order", api.Order)
	}

	if api.Output == "" {
		urlQueryParams.Add("output", DefaultOutputFormat)
	} else {
		urlQueryParams.Add("output", api.Output)
	}

	urlQueryParams.Add("chunk", strconv.FormatUint(api.ChunkNumber, 10))
	urlQueryParams.Add("chunk_size", strconv.FormatUint(api.ChunkSize, 10))
	return urlQueryParams
}

// GetFullQueryURL returns the full url for interacting with Audisto API, INCLUDING query params
func (api *AudistoAPIClient) GetFullQueryURL() string {
	return api.GetQueryParams().Encode()
}

// SetChunkSize set AudistoAPI.ChunkSize to a new size
func (api *AudistoAPIClient) SetChunkSize(size uint64) {
	if size == 0 {
		api.ChunkSize = DefaultChunkSize
	} else {
		api.ChunkSize = size
	}
}

// SetNextChunkNumber set AudistoAPI.ChunkNumber to the next chunk number
func (api *AudistoAPIClient) SetNextChunkNumber(number uint64) {
	api.ChunkNumber = number
}

// GetRequestMethod returns the HTTP request method, GET (by default)
func (api *AudistoAPIClient) GetRequestMethod() string {
	if api.requestMethod == "" {
		return DefaultRequestMethod
	}
	return api.requestMethod
}

// SetRequestMethod sets the HTTP request method for interacting with Audisto API
// Allowed method: GET, POST, PATCH, DELETE
func (api *AudistoAPIClient) SetRequestMethod(method string) error {
	method = strings.ToUpper(method)

	if method != "GET" && method != "POST" && method != "PATCH" && method != "DELETE" {
		return fmt.Errorf("Method not supported: %s", method)
	}
	api.requestMethod = method
	return nil
}

// GetRequestURL returns a validated instance of url.URL, and an error if the validation fails
func (api *AudistoAPIClient) GetRequestURL() (*url.URL, error) {
	domain := api.GetBaseURL()
	requestURL, err := url.Parse(domain)
	if err != nil {
		return requestURL, err
	}
	requestURL.Path = api.GetRelativePath()
	requestURL.RawQuery = api.GetQueryParams().Encode()
	return requestURL, nil
}
<|endoftext|>


<commit_before>package encoding

import (
	"image"
	"reflect"
	"testing"
	"time"
)

var encodeExpected = map[string]interface{}{
	"Level0":  int64(1),
	"Level1b": int64(2),
	"Level1c": int64(3),
	"Level1a": int64(5),
	"LEVEL1B": int64(6),
	"e": map[string]interface{}{
		"Level1a": int64(8),
		"Level1b": int64(9),
		"Level1c": int64(10),
		"Level1d": int64(11),
		"x":       int64(12),
	},
	"Loop1": int64(13),
	"Loop2": int64(14),
	"X":     int64(15),
	"Y":     int64(16),
	"Z":     int64(17),
}

func TestEncode(t *testing.T) {
	// Top is defined in decoder_test.go
	var in = Top{
		Level0: 1,
		Embed0: Embed0{
			Level1b: 2,
			Level1c: 3,
		},
		Embed0a: &Embed0a{
			Level1a: 5,
			Level1b: 6,
		},
		Embed0b: &Embed0b{
			Level1a: 8,
			Level1b: 9,
			Level1c: 10,
			Level1d: 11,
			Level1e: 12,
		},
		Loop: Loop{
			Loop1: 13,
			Loop2: 14,
		},
		Embed0p: Embed0p{
			Point: image.Point{X: 15, Y: 16},
		},
		Embed0q: Embed0q{
			Point: Point{Z: 17},
		},
	}

	got, err := Encode(&in)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, encodeExpected) {
		t.Errorf(" got: %v\nwant: %v\n", got, encodeExpected)
	}
}

type Optionals struct {
	Sr string `gorethink:"sr"`
	So string `gorethink:"so,omitempty"`
	Sw string `gorethink:"-"`

	Ir int `gorethink:"omitempty"` // actually named omitempty, not an option
	Io int `gorethink:"io,omitempty"`

	Tr time.Time `gorethink:"tr"`
	To time.Time `gorethink:"to,omitempty"`

	Slr []string `gorethink:"slr"`
	Slo []string `gorethink:"slo,omitempty"`

	Mr map[string]interface{} `gorethink:"mr"`
	Mo map[string]interface{} `gorethink:",omitempty"`
}

var optionalsExpected = map[string]interface{}{
	"sr":        "",
	"omitempty": int64(0),
	"tr":        map[string]interface{}{"$reql_type$": "TIME", "epoch_time": 0, "timezone": "+00:00"},
	"slr":       []interface{}{},
	"mr":        map[string]interface{}{},
}

func TestOmitEmpty(t *testing.T) {
	var o Optionals
	o.Sw = "something"
	o.Tr = time.Unix(0, 0).In(time.UTC)
	o.Mr = map[string]interface{}{}
	o.Mo = map[string]interface{}{}

	got, err := Encode(&o)
	if err != nil {
		t.Fatal(err)
	}
	if !jsonEqual(got, optionalsExpected) {
		t.Errorf("\ngot:  %#v\nwant: %#v\n", got, optionalsExpected)
	}
}

type IntType int

type MyStruct struct {
	IntType
}

func TestAnonymousNonstruct(t *testing.T) {
	var i IntType = 11
	a := MyStruct{i}
	var want = map[string]interface{}{"IntType": int64(11)}

	got, err := Encode(a)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

func TestEncodePointer(t *testing.T) {
	v := Pointer{PPoint: &Point{Z: 1}, Point: Point{Z: 2}}
	var want = map[string]interface{}{
		"PPoint": map[string]interface{}{"Z": int64(1)},
		"Point":  map[string]interface{}{"Z": int64(2)},
	}

	got, err := Encode(v)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

func TestEncodeNilPointer(t *testing.T) {
	v := Pointer{PPoint: nil, Point: Point{Z: 2}}
	var want = map[string]interface{}{
		"PPoint": nil,
		"Point":  map[string]interface{}{"Z": int64(2)},
	}

	got, err := Encode(v)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

type BugA struct {
	S string
}

type BugB struct {
	BugA
	S string
}

type BugC struct {
	S string
}

// Legal Go: We never use the repeated embedded field (S).
type BugX struct {
	A int
	BugA
	BugB
}

// Issue 5245.
func TestEmbeddedBug(t *testing.T) {
	v := BugB{
		BugA{"A"},
		"B",
	}
	got, err := Encode(v)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want := map[string]interface{}{"S": "B"}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
	// Now check that the duplicate field, S, does not appear.
	x := BugX{
		A: 23,
	}
	got, err = Encode(x)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want = map[string]interface{}{"A": int64(23)}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
}

type BugD struct { // Same as BugA after tagging.
	XXX string `gorethink:"S"`
}

// BugD's tagged S field should dominate BugA's.
type BugY struct {
	BugA
	BugD
}

// Test that a field with a tag dominates untagged fields.
func TestTaggedFieldDominates(t *testing.T) {
	v := BugY{
		BugA{"BugA"},
		BugD{"BugD"},
	}
	got, err := Encode(v)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want := map[string]interface{}{"S": "BugD"}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
}

// There are no tags here, so S should not appear.
type BugZ struct {
	BugA
	BugC
	BugY // Contains a tagged S field through BugD; should not dominate.
}

func TestDuplicatedFieldDisappears(t *testing.T) {
	v := BugZ{
		BugA{"BugA"},
		BugC{"BugC"},
		BugY{
			BugA{"nested BugA"},
			BugD{"nested BugD"},
		},
	}
	got, err := Encode(v)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want := map[string]interface{}{}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
}

func TestEncodeMapIntKeys(t *testing.T) {
	input := map[int]int{1: 1, 2: 2, 3: 3}
	want := map[string]int{"1": 1, "2": 2, "3": 3}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type RefA struct {
	ID string `gorethink:"id,omitempty"`
	B  *RefB  `gorethink:"b_id,reference" gorethink_ref:"id"`
}

type RefB struct {
	ID   string `gorethink:"id,omitempty"`
	Name string `gorethink:"name"`
}

func TestReferenceField(t *testing.T) {
	input := RefA{"1", &RefB{"2", "Name"}}
	want := map[string]interface{}{"id": "1", "b_id": "2"}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type RefC struct {
	ID string `gorethink:"id,omitempty"`
	B  *RefB  `gorethink:"b_id,reference" gorethink_ref:"b_id"`
}

func TestReferenceFieldMissing(t *testing.T) {
	input := RefC{"1", &RefB{"2", "Name"}}

	_, err := Encode(input)
	if err == nil {
		t.Errorf("expected non-nil error but got nil")
	}
}

type RefD struct {
	ID string `gorethink:"id,omitempty"`
	B  string `gorethink:"b_id,reference" gorethink_ref:"b_id"`
}

func TestReferenceFieldInvalid(t *testing.T) {
	input := RefD{"1", "B"}

	_, err := Encode(input)
	if err == nil {
		t.Errorf("expected non-nil error but got nil")
	}
}

type RefE struct {
	ID   string  `gorethink:"id,omitempty"`
	FIDs *[]RefF `gorethink:"f_ids,reference" gorethink_ref:"id"`
}

type RefF struct {
	ID   string `gorethink:"id,omitempty"`
	Name string `gorethink:"name"`
}

func TestReferenceFieldArray(t *testing.T) {
	input := RefE{"1", &[]RefF{RefF{"2", "Name2"}, RefF{"3", "Name3"}}}
	want := map[string]interface{}{"id": "1", "f_ids": []string{"2", "3"}}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

func TestEncodeBytes(t *testing.T) {
	type BytesStruct struct {
		A []byte
		B [1]byte
	}

	input := BytesStruct{[]byte("A"), [1]byte{'B'}}
	want := map[string]interface{}{
		"A": map[string]interface{}{"$reql_type$": "BINARY", "data": "QQ=="},
		"B": map[string]interface{}{"$reql_type$": "BINARY", "data": "Qg=="},
	}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type Compound struct {
	PartA string `gorethink:"id[0]"`
	PartB string `gorethink:"id[1]"`
	ErrA  string `gorethink:"err_a[]"`
	ErrB  string `gorethink:"err_b["`
	ErrC  string `gorethink:"err_c]"`
}

func TestEncodeCompound(t *testing.T) {
	input := Compound{"1", "2", "3", "4", "5"}
	want := map[string]interface{}{"id": []string{"1", "2"}, "err_a[]": "3", "err_b[": "4", "err_c]": "5"}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type CompoundRef struct {
	PartA string `gorethink:"id[0]"`
	PartB *RefB  `gorethink:"id[1],reference" gorethink_ref:"id"`
}

func TestEncodeCompoundRef(t *testing.T) {
	input := CompoundRef{"1", &RefB{"2", "Name"}}
	want := map[string]interface{}{"id": []string{"1", "2"}}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

func TestEncodeNilSlice(t *testing.T) {
	input := SliceStruct{}
	want := map[string]interface{}{"X": []string(nil)}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}
<commit_msg>Fix test<commit_after>package encoding

import (
	"image"
	"reflect"
	"testing"
	"time"
)

var encodeExpected = map[string]interface{}{
	"Level0":  int64(1),
	"Level1b": int64(2),
	"Level1c": int64(3),
	"Level1a": int64(5),
	"LEVEL1B": int64(6),
	"e": map[string]interface{}{
		"Level1a": int64(8),
		"Level1b": int64(9),
		"Level1c": int64(10),
		"Level1d": int64(11),
		"x":       int64(12),
	},
	"Loop1": int64(13),
	"Loop2": int64(14),
	"X":     int64(15),
	"Y":     int64(16),
	"Z":     int64(17),
}

func TestEncode(t *testing.T) {
	// Top is defined in decoder_test.go
	var in = Top{
		Level0: 1,
		Embed0: Embed0{
			Level1b: 2,
			Level1c: 3,
		},
		Embed0a: &Embed0a{
			Level1a: 5,
			Level1b: 6,
		},
		Embed0b: &Embed0b{
			Level1a: 8,
			Level1b: 9,
			Level1c: 10,
			Level1d: 11,
			Level1e: 12,
		},
		Loop: Loop{
			Loop1: 13,
			Loop2: 14,
		},
		Embed0p: Embed0p{
			Point: image.Point{X: 15, Y: 16},
		},
		Embed0q: Embed0q{
			Point: Point{Z: 17},
		},
	}

	got, err := Encode(&in)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, encodeExpected) {
		t.Errorf(" got: %v\nwant: %v\n", got, encodeExpected)
	}
}

type Optionals struct {
	Sr string `gorethink:"sr"`
	So string `gorethink:"so,omitempty"`
	Sw string `gorethink:"-"`

	Ir int `gorethink:"omitempty"` // actually named omitempty, not an option
	Io int `gorethink:"io,omitempty"`

	Tr time.Time `gorethink:"tr"`
	To time.Time `gorethink:"to,omitempty"`

	Slr []string `gorethink:"slr"`
	Slo []string `gorethink:"slo,omitempty"`

	Mr map[string]interface{} `gorethink:"mr"`
	Mo map[string]interface{} `gorethink:",omitempty"`
}

var optionalsExpected = map[string]interface{}{
	"sr":        "",
	"omitempty": int64(0),
	"tr":        map[string]interface{}{"$reql_type$": "TIME", "epoch_time": 0, "timezone": "+00:00"},
	"slr":       []interface{}(nil),
	"mr":        map[string]interface{}{},
}

func TestOmitEmpty(t *testing.T) {
	var o Optionals
	o.Sw = "something"
	o.Tr = time.Unix(0, 0).In(time.UTC)
	o.Mr = map[string]interface{}{}
	o.Mo = map[string]interface{}{}

	got, err := Encode(&o)
	if err != nil {
		t.Fatal(err)
	}
	if !jsonEqual(got, optionalsExpected) {
		t.Errorf("\ngot:  %#v\nwant: %#v\n", got, optionalsExpected)
	}
}

type IntType int

type MyStruct struct {
	IntType
}

func TestAnonymousNonstruct(t *testing.T) {
	var i IntType = 11
	a := MyStruct{i}
	var want = map[string]interface{}{"IntType": int64(11)}

	got, err := Encode(a)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

func TestEncodePointer(t *testing.T) {
	v := Pointer{PPoint: &Point{Z: 1}, Point: Point{Z: 2}}
	var want = map[string]interface{}{
		"PPoint": map[string]interface{}{"Z": int64(1)},
		"Point":  map[string]interface{}{"Z": int64(2)},
	}

	got, err := Encode(v)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

func TestEncodeNilPointer(t *testing.T) {
	v := Pointer{PPoint: nil, Point: Point{Z: 2}}
	var want = map[string]interface{}{
		"PPoint": nil,
		"Point":  map[string]interface{}{"Z": int64(2)},
	}

	got, err := Encode(v)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}
}

type BugA struct {
	S string
}

type BugB struct {
	BugA
	S string
}

type BugC struct {
	S string
}

// Legal Go: We never use the repeated embedded field (S).
type BugX struct {
	A int
	BugA
	BugB
}

// Issue 5245.
func TestEmbeddedBug(t *testing.T) {
	v := BugB{
		BugA{"A"},
		"B",
	}
	got, err := Encode(v)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want := map[string]interface{}{"S": "B"}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
	// Now check that the duplicate field, S, does not appear.
	x := BugX{
		A: 23,
	}
	got, err = Encode(x)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want = map[string]interface{}{"A": int64(23)}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
}

type BugD struct { // Same as BugA after tagging.
	XXX string `gorethink:"S"`
}

// BugD's tagged S field should dominate BugA's.
type BugY struct {
	BugA
	BugD
}

// Test that a field with a tag dominates untagged fields.
func TestTaggedFieldDominates(t *testing.T) {
	v := BugY{
		BugA{"BugA"},
		BugD{"BugD"},
	}
	got, err := Encode(v)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want := map[string]interface{}{"S": "BugD"}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
}

// There are no tags here, so S should not appear.
type BugZ struct {
	BugA
	BugC
	BugY // Contains a tagged S field through BugD; should not dominate.
}

func TestDuplicatedFieldDisappears(t *testing.T) {
	v := BugZ{
		BugA{"BugA"},
		BugC{"BugC"},
		BugY{
			BugA{"nested BugA"},
			BugD{"nested BugD"},
		},
	}
	got, err := Encode(v)
	if err != nil {
		t.Fatal("Encode:", err)
	}
	want := map[string]interface{}{}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("Encode: got %v want %v", got, want)
	}
}

func TestEncodeMapIntKeys(t *testing.T) {
	input := map[int]int{1: 1, 2: 2, 3: 3}
	want := map[string]int{"1": 1, "2": 2, "3": 3}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type RefA struct {
	ID string `gorethink:"id,omitempty"`
	B  *RefB  `gorethink:"b_id,reference" gorethink_ref:"id"`
}

type RefB struct {
	ID   string `gorethink:"id,omitempty"`
	Name string `gorethink:"name"`
}

func TestReferenceField(t *testing.T) {
	input := RefA{"1", &RefB{"2", "Name"}}
	want := map[string]interface{}{"id": "1", "b_id": "2"}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type RefC struct {
	ID string `gorethink:"id,omitempty"`
	B  *RefB  `gorethink:"b_id,reference" gorethink_ref:"b_id"`
}

func TestReferenceFieldMissing(t *testing.T) {
	input := RefC{"1", &RefB{"2", "Name"}}

	_, err := Encode(input)
	if err == nil {
		t.Errorf("expected non-nil error but got nil")
	}
}

type RefD struct {
	ID string `gorethink:"id,omitempty"`
	B  string `gorethink:"b_id,reference" gorethink_ref:"b_id"`
}

func TestReferenceFieldInvalid(t *testing.T) {
	input := RefD{"1", "B"}

	_, err := Encode(input)
	if err == nil {
		t.Errorf("expected non-nil error but got nil")
	}
}

type RefE struct {
	ID   string  `gorethink:"id,omitempty"`
	FIDs *[]RefF `gorethink:"f_ids,reference" gorethink_ref:"id"`
}

type RefF struct {
	ID   string `gorethink:"id,omitempty"`
	Name string `gorethink:"name"`
}

func TestReferenceFieldArray(t *testing.T) {
	input := RefE{"1", &[]RefF{RefF{"2", "Name2"}, RefF{"3", "Name3"}}}
	want := map[string]interface{}{"id": "1", "f_ids": []string{"2", "3"}}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

func TestEncodeBytes(t *testing.T) {
	type BytesStruct struct {
		A []byte
		B [1]byte
	}

	input := BytesStruct{[]byte("A"), [1]byte{'B'}}
	want := map[string]interface{}{
		"A": map[string]interface{}{"$reql_type$": "BINARY", "data": "QQ=="},
		"B": map[string]interface{}{"$reql_type$": "BINARY", "data": "Qg=="},
	}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type Compound struct {
	PartA string `gorethink:"id[0]"`
	PartB string `gorethink:"id[1]"`
	ErrA  string `gorethink:"err_a[]"`
	ErrB  string `gorethink:"err_b["`
	ErrC  string `gorethink:"err_c]"`
}

func TestEncodeCompound(t *testing.T) {
	input := Compound{"1", "2", "3", "4", "5"}
	want := map[string]interface{}{"id": []string{"1", "2"}, "err_a[]": "3", "err_b[": "4", "err_c]": "5"}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

type CompoundRef struct {
	PartA string `gorethink:"id[0]"`
	PartB *RefB  `gorethink:"id[1],reference" gorethink_ref:"id"`
}

func TestEncodeCompoundRef(t *testing.T) {
	input := CompoundRef{"1", &RefB{"2", "Name"}}
	want := map[string]interface{}{"id": []string{"1", "2"}}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}

func TestEncodeNilSlice(t *testing.T) {
	input := SliceStruct{}
	want := map[string]interface{}{"X": []string(nil)}

	out, err := Encode(input)
	if err != nil {
		t.Errorf("got error %v, expected nil", err)
	}
	if !jsonEqual(out, want) {
		t.Errorf("got %q, want %q", out, want)
	}
}
<|endoftext|>


<commit_before>package resources

import (
	"context"
	"encoding/json"
	"strings"

	awsv2 "github.com/aws/aws-sdk-go-v2/aws"
	awsv2S3 "github.com/aws/aws-sdk-go-v2/service/s3"
	awsv2S3Types "github.com/aws/aws-sdk-go-v2/service/s3/types"

	gof "github.com/awslabs/goformation/v5/cloudformation"
	"github.com/rs/zerolog"
)

// S3LambdaEventSourceResourceRequest is what the UserProperties
// should be set to in the CustomResource invocation
type S3LambdaEventSourceResourceRequest struct {
	CustomResourceRequest
	BucketArn       string
	Events          []string
	LambdaTargetArn string
	Filter          *awsv2S3Types.NotificationConfigurationFilter `json:"Filter,omitempty"`
}

////////////////////////////////////////////////////////////////////////////////
// TODO - update all the custom resources to use this approach so that
// the properties object is properly serialized. We'll also need to deserialize
// the request for the custom handler.
////////////////////////////////////////////////////////////////////////////////

// S3LambdaEventSourceResource manages registering a Lambda function with S3 event
type S3LambdaEventSourceResource struct {
	gof.CustomResource
}

// IAMPrivileges returns the IAM privs for this custom action
func (command *S3LambdaEventSourceResource) IAMPrivileges() []string {
	return []string{"s3:GetBucketLocation",
		"s3:GetBucketNotification",
		"s3:PutBucketNotification",
		"s3:GetBucketNotificationConfiguration",
		"s3:PutBucketNotificationConfiguration"}
}

func (command S3LambdaEventSourceResource) updateNotification(isTargetActive bool,
	awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {

	s3EventRequest := S3LambdaEventSourceResourceRequest{}
	unmarshalErr := json.Unmarshal(event.ResourceProperties, &s3EventRequest)
	if unmarshalErr != nil {
		return nil, unmarshalErr
	}

	s3Svc := awsv2S3.NewFromConfig(awsConfig)
	bucketParts := strings.Split(s3EventRequest.BucketArn, ":")
	bucketName := bucketParts[len(bucketParts)-1]

	params := &awsv2S3.GetBucketNotificationConfigurationInput{
		Bucket: awsv2.String(bucketName),
	}
	config, configErr := s3Svc.GetBucketNotificationConfiguration(context.Background(), params)
	if nil != configErr {
		return nil, configErr
	}
	// First thing, eliminate existing references...
	var lambdaConfigurations []awsv2S3Types.LambdaFunctionConfiguration
	for _, eachLambdaConfig := range config.LambdaFunctionConfigurations {
		if *eachLambdaConfig.LambdaFunctionArn != s3EventRequest.LambdaTargetArn {
			lambdaConfigurations = append(lambdaConfigurations, eachLambdaConfig)
		}
	}

	if isTargetActive {
		var eventPtrs []awsv2S3Types.Event
		for _, eachString := range s3EventRequest.Events {
			eventPtrs = append(eventPtrs, awsv2S3Types.Event(eachString))
		}
		commandConfig := awsv2S3Types.LambdaFunctionConfiguration{
			LambdaFunctionArn: awsv2.String(s3EventRequest.LambdaTargetArn),
			Events:            eventPtrs,
		}
		if s3EventRequest.Filter != nil {
			commandConfig.Filter = s3EventRequest.Filter
		}
		lambdaConfigurations = append(lambdaConfigurations, commandConfig)
	}

	putBucketNotificationConfigurationInput := &awsv2S3.PutBucketNotificationConfigurationInput{
		Bucket: awsv2.String(bucketName),
		NotificationConfiguration: &awsv2S3Types.NotificationConfiguration{
			LambdaFunctionConfigurations: lambdaConfigurations,
		},
	}

	logger.Debug().
		Interface("PutBucketNotificationConfigurationInput", putBucketNotificationConfigurationInput).
		Msg("Updating bucket configuration")

	_, putErr := s3Svc.PutBucketNotificationConfiguration(context.Background(), putBucketNotificationConfigurationInput)
	return nil, putErr
}

// Create implements the custom resource create operation
func (command S3LambdaEventSourceResource) Create(ctx context.Context, awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {
	return command.updateNotification(true, awsConfig, event, logger)
}

// Update implements the custom resource update operation
func (command S3LambdaEventSourceResource) Update(ctx context.Context, awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {
	return command.updateNotification(true, awsConfig, event, logger)
}

// Delete implements the custom resource delete operation
func (command S3LambdaEventSourceResource) Delete(ctx context.Context, awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {
	return command.updateNotification(false, awsConfig, event, logger)
}
<commit_msg>Remove obsolete TODO<commit_after>package resources

import (
	"context"
	"encoding/json"
	"strings"

	awsv2 "github.com/aws/aws-sdk-go-v2/aws"
	awsv2S3 "github.com/aws/aws-sdk-go-v2/service/s3"
	awsv2S3Types "github.com/aws/aws-sdk-go-v2/service/s3/types"

	gof "github.com/awslabs/goformation/v5/cloudformation"
	"github.com/rs/zerolog"
)

// S3LambdaEventSourceResourceRequest is what the UserProperties
// should be set to in the CustomResource invocation
type S3LambdaEventSourceResourceRequest struct {
	CustomResourceRequest
	BucketArn       string
	Events          []string
	LambdaTargetArn string
	Filter          *awsv2S3Types.NotificationConfigurationFilter `json:"Filter,omitempty"`
}

// S3LambdaEventSourceResource manages registering a Lambda function with S3 event
type S3LambdaEventSourceResource struct {
	gof.CustomResource
}

// IAMPrivileges returns the IAM privs for this custom action
func (command *S3LambdaEventSourceResource) IAMPrivileges() []string {
	return []string{"s3:GetBucketLocation",
		"s3:GetBucketNotification",
		"s3:PutBucketNotification",
		"s3:GetBucketNotificationConfiguration",
		"s3:PutBucketNotificationConfiguration"}
}

func (command S3LambdaEventSourceResource) updateNotification(isTargetActive bool,
	awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {

	s3EventRequest := S3LambdaEventSourceResourceRequest{}
	unmarshalErr := json.Unmarshal(event.ResourceProperties, &s3EventRequest)
	if unmarshalErr != nil {
		return nil, unmarshalErr
	}

	s3Svc := awsv2S3.NewFromConfig(awsConfig)
	bucketParts := strings.Split(s3EventRequest.BucketArn, ":")
	bucketName := bucketParts[len(bucketParts)-1]

	params := &awsv2S3.GetBucketNotificationConfigurationInput{
		Bucket: awsv2.String(bucketName),
	}
	config, configErr := s3Svc.GetBucketNotificationConfiguration(context.Background(), params)
	if nil != configErr {
		return nil, configErr
	}
	// First thing, eliminate existing references...
	var lambdaConfigurations []awsv2S3Types.LambdaFunctionConfiguration
	for _, eachLambdaConfig := range config.LambdaFunctionConfigurations {
		if *eachLambdaConfig.LambdaFunctionArn != s3EventRequest.LambdaTargetArn {
			lambdaConfigurations = append(lambdaConfigurations, eachLambdaConfig)
		}
	}

	if isTargetActive {
		var eventPtrs []awsv2S3Types.Event
		for _, eachString := range s3EventRequest.Events {
			eventPtrs = append(eventPtrs, awsv2S3Types.Event(eachString))
		}
		commandConfig := awsv2S3Types.LambdaFunctionConfiguration{
			LambdaFunctionArn: awsv2.String(s3EventRequest.LambdaTargetArn),
			Events:            eventPtrs,
		}
		if s3EventRequest.Filter != nil {
			commandConfig.Filter = s3EventRequest.Filter
		}
		lambdaConfigurations = append(lambdaConfigurations, commandConfig)
	}

	putBucketNotificationConfigurationInput := &awsv2S3.PutBucketNotificationConfigurationInput{
		Bucket: awsv2.String(bucketName),
		NotificationConfiguration: &awsv2S3Types.NotificationConfiguration{
			LambdaFunctionConfigurations: lambdaConfigurations,
		},
	}

	logger.Debug().
		Interface("PutBucketNotificationConfigurationInput", putBucketNotificationConfigurationInput).
		Msg("Updating bucket configuration")

	_, putErr := s3Svc.PutBucketNotificationConfiguration(context.Background(), putBucketNotificationConfigurationInput)
	return nil, putErr
}

// Create implements the custom resource create operation
func (command S3LambdaEventSourceResource) Create(ctx context.Context, awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {
	return command.updateNotification(true, awsConfig, event, logger)
}

// Update implements the custom resource update operation
func (command S3LambdaEventSourceResource) Update(ctx context.Context, awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {
	return command.updateNotification(true, awsConfig, event, logger)
}

// Delete implements the custom resource delete operation
func (command S3LambdaEventSourceResource) Delete(ctx context.Context, awsConfig awsv2.Config,
	event *CloudFormationLambdaEvent,
	logger *zerolog.Logger) (map[string]interface{}, error) {
	return command.updateNotification(false, awsConfig, event, logger)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"github.com/vladpereskokov/Technopark_HighLoad-nginx/src/handler"
	modelConfig "github.com/vladpereskokov/Technopark_HighLoad-nginx/src/models/configs"
	"log"
	"net"
	"os"
	"strconv"
)

type Server struct {
	Network  string
	Protocol string
	Host     string
	Port     string
	IsSetup  bool
}

func (server *Server) CreateServer(config modelConfig.Server) {
	server.setNetwork(config.Network)
	server.setProtocol(config.Protocol)
	server.setHost(config.Host)
	server.setPort(strconv.Itoa(config.Port))
	server.setSetup(true)
}

func (server *Server) Start() {
	if server.IsSetup {
		listener, err := net.Listen(server.Network, ":"+server.Port)
		if err != nil {
			panic("Failed start server: " + err.Error())
		}

		defer listener.Close()

		log.Print("Server started at " + server.Port + " port")

		ch := make(chan net.Conn)

		handle := handler.Handler{}

		for i := 0; i < 4; i++ {
			go handle.Start(ch)
			println("Created worker...")
		}

		for {
			conn, err := listener.Accept()
			if err != nil {
				fmt.Println("Error accepting: ", err.Error())
				os.Exit(1)
			}

			ch <- conn
		}
	} else {
		panic("hop hey lalaley")
	}
}

func (server *Server) setNetwork(network string) {
	server.Network = network
}

func (server *Server) setProtocol(protocol string) {
	server.Protocol = protocol
}

func (server *Server) setHost(host string) {
	server.Host = host
}

func (server *Server) setPort(port string) {
	server.Port = port
}

func (server *Server) setSetup(isSetup bool) {
	server.IsSetup = isSetup
}
<commit_msg>change panic<commit_after>package server

import (
	"fmt"
	"github.com/vladpereskokov/Technopark_HighLoad-nginx/src/handler"
	modelConfig "github.com/vladpereskokov/Technopark_HighLoad-nginx/src/models/configs"
	"log"
	"net"
	"os"
	"strconv"
)

type Server struct {
	Network  string
	Protocol string
	Host     string
	Port     string
	IsSetup  bool
}

func (server *Server) CreateServer(config modelConfig.Server) {
	server.setNetwork(config.Network)
	server.setProtocol(config.Protocol)
	server.setHost(config.Host)
	server.setPort(strconv.Itoa(config.Port))
	server.setSetup(true)
}

func (server *Server) Start() {
	if server.IsSetup {
		listener, err := net.Listen(server.Network, ":"+server.Port)
		if err != nil {
			panic("Failed start server: " + err.Error())
		}

		defer listener.Close()

		log.Print("Server started at " + server.Port + " port")

		ch := make(chan net.Conn)

		handle := handler.Handler{}

		for i := 0; i < 4; i++ {
			go handle.Start(ch)
			println("Created worker...")
		}

		for {
			conn, err := listener.Accept()
			if err != nil {
				fmt.Println("Error accepting: ", err.Error())
				os.Exit(1)
			}

			ch <- conn
		}
	} else {
		panic("Server is not setup")
	}
}

func (server *Server) setNetwork(network string) {
	server.Network = network
}

func (server *Server) setProtocol(protocol string) {
	server.Protocol = protocol
}

func (server *Server) setHost(host string) {
	server.Host = host
}

func (server *Server) setPort(port string) {
	server.Port = port
}

func (server *Server) setSetup(isSetup bool) {
	server.IsSetup = isSetup
}
<|endoftext|>