<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package swim

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math/rand"
	"sort"
	"sync"
	"time"

	"github.com/benbjohnson/clock"
	"github.com/dgryski/go-farm"
	"github.com/uber/ringpop-go/util"
)

// A memberlist contains the membership for a node
type memberlist struct {
	node  *Node
	local *Member

	members struct {
		list      []*Member
		byAddress map[string]*Member
		checksum  uint32
		sync.RWMutex
	}
}

// newMemberlist returns a new member list
func newMemberlist(n *Node) *memberlist {
	m := &memberlist{
		node: n,
	}

	m.members.byAddress = make(map[string]*Member)

	return m
}

func (m *memberlist) Checksum() uint32 {
	m.members.Lock()
	checksum := m.members.checksum
	m.members.Unlock()

	return checksum
}

// computes membership checksum
func (m *memberlist) ComputeChecksum() {
	startTime := time.Now()
	m.members.Lock()
	checksum := farm.Fingerprint32([]byte(m.GenChecksumString()))
	m.members.checksum = checksum
	m.members.Unlock()
	m.node.emit(ChecksumComputeEvent{
		Duration: time.Now().Sub(startTime),
		Checksum: checksum,
	})
}

// generates string to use when computing checksum
func (m *memberlist) GenChecksumString() string {
	var strings sort.StringSlice

	for _, member := range m.members.list {
		s := fmt.Sprintf("%s%s%v", member.Address, member.Status, member.Incarnation)
		strings = append(strings, s)
	}

	strings.Sort()

	buffer := bytes.NewBuffer([]byte{})
	for _, str := range strings {
		buffer.WriteString(str)
		buffer.WriteString(";")
	}

	return buffer.String()
}

// returns the member at a specific address
func (m *memberlist) Member(address string) (*Member, bool) {
	m.members.RLock()
	member, ok := m.members.byAddress[address]
	m.members.RUnlock()

	return member, ok
}

func (m *memberlist) MemberAt(i int) *Member {
	m.members.RLock()
	member := m.members.list[i]
	m.members.RUnlock()

	return member
}

func (m *memberlist) NumMembers() int {
	m.members.RLock()
	n := len(m.members.list)
	m.members.RUnlock()

	return n
}

// returns whether or not a member is pingable
func (m *memberlist) Pingable(member Member) bool {
	return member.Address != m.local.Address &&
		(member.Status == Alive || member.Status == Suspect)

}

// returns the number of pingable members in the memberlist
func (m *memberlist) NumPingableMembers() (n int) {
	m.members.Lock()
	for _, member := range m.members.list {
		if m.Pingable(*member) {
			n++
		}
	}
	m.members.Unlock()

	return n
}

// returns n pingable members in the member list
func (m *memberlist) RandomPingableMembers(n int, excluding map[string]bool) []*Member {
	var members []*Member

	m.members.RLock()
	for _, member := range m.members.list {
		if m.Pingable(*member) && !excluding[member.Address] {
			members = append(members, member)
		}
	}
	m.members.RUnlock()

	// shuffle members and take first n
	members = shuffle(members)

	if n > len(members) {
		return members
	}
	return members[:n]
}

// returns an immutable slice of members representing the current state of the membership
func (m *memberlist) GetMembers() (members []Member) {
	m.members.RLock()
	for _, member := range m.members.list {
		members = append(members, *member)
	}
	m.members.RUnlock()

	return
}

// Reincarnate sets the status of the node to Alive and updates the incarnation
// number. It adds the change to the disseminator as well.
func (m *memberlist) Reincarnate() []Change {
	return m.MakeAlive(m.node.address, nowInMillis(m.node.clock))
}

func (m *memberlist) MakeAlive(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Alive})
	return m.MakeChange(address, incarnation, Alive)
}

func (m *memberlist) MakeSuspect(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Suspect})
	return m.MakeChange(address, incarnation, Suspect)
}

func (m *memberlist) MakeFaulty(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Faulty})
	return m.MakeChange(address, incarnation, Faulty)
}

func (m *memberlist) MakeLeave(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Leave})
	return m.MakeChange(address, incarnation, Leave)
}

// makes a change to the member list
func (m *memberlist) MakeChange(address string, incarnation int64, status string) []Change {
	if m.local == nil {
		m.local = &Member{
			Address:     m.node.Address(),
			Incarnation: util.TimeNowMS(),
			Status:      Alive,
		}
	}

	return m.Update([]Change{Change{
		Source:            m.local.Address,
		SourceIncarnation: m.local.Incarnation,
		Address:           address,
		Incarnation:       incarnation,
		Status:            status,
		Timestamp:         util.Timestamp(time.Now()),
	}})
}

// updates the member list with the slice of changes, applying selectively
func (m *memberlist) Update(changes []Change) (applied []Change) {
	if m.node.Stopped() || len(changes) == 0 {
		return nil
	}

	m.node.emit(MemberlistChangesReceivedEvent{changes})

	m.members.Lock()

	for _, change := range changes {
		member, ok := m.members.byAddress[change.Address]

		// first time member has been seen, take change wholesale
		if !ok {
			m.Apply(change)
			applied = append(applied, change)
			continue
		}

		// if change is local override, reassert member is alive
		if member.localOverride(m.node.Address(), change) {
			m.node.emit(RefuteUpdateEvent{})
			overrideChange := Change{
				Source:            change.Source,
				SourceIncarnation: change.SourceIncarnation,
				Address:           change.Address,
				Incarnation:       nowInMillis(m.node.clock),
				Status:            Alive,
				Timestamp:         util.Timestamp(time.Now()),
			}

			m.Apply(overrideChange)
			applied = append(applied, overrideChange)
			continue
		}

		// if non-local override, apply change wholesale
		if member.nonLocalOverride(change) {
			m.Apply(change)
			applied = append(applied, change)
		}
	}

	m.members.Unlock()

	if len(applied) > 0 {
		oldChecksum := m.Checksum()
		m.ComputeChecksum()
		m.node.emit(MemberlistChangesAppliedEvent{
			Changes:     applied,
			OldChecksum: oldChecksum,
			NewChecksum: m.Checksum(),
			NumMembers:  m.NumMembers(),
		})
		m.node.handleChanges(applied)
		m.node.rollup.TrackUpdates(applied)
	}

	return applied
}

// AddJoinList adds the list to the membership with the Update
// function. However, as a side effect, Update adds changes to
// the disseminator as well. Since we don't want to disseminate
// the potentially very large join lists, we clear all the
// changes from the disseminator, except for the one change
// that refers to the make-alive of this node.
func (m *memberlist) AddJoinList(list []Change) {
	applied := m.Update(list)
	for _, member := range applied {
		if member.Address == m.node.Address() {
			continue
		}
		m.node.disseminator.ClearChange(member.Address)
	}
}

// gets a random position in [0, length of member list)
func (m *memberlist) getJoinPosition() int {
	l := len(m.members.list)
	if l == 0 {
		return l
	}
	return rand.Intn(l)
}

// applies a change directly to the member list
func (m *memberlist) Apply(change Change) {
	member, ok := m.members.byAddress[change.Address]

	if !ok {
		member = &Member{
			Address:     change.Address,
			Status:      change.Status,
			Incarnation: change.Incarnation,
		}

		if member.Address == m.node.Address() {
			m.local = member
		}

		m.members.byAddress[change.Address] = member
		i := m.getJoinPosition()
		m.members.list = append(m.members.list[:i], append([]*Member{member}, m.members.list[i:]...)...)
	}

	member.Lock()
	member.Status = change.Status
	member.Incarnation = change.Incarnation
	member.Unlock()
}

// shuffles the member list
func (m *memberlist) Shuffle() {
	m.members.Lock()
	m.members.list = shuffle(m.members.list)
	m.members.Unlock()
}

// String returns a JSON string
func (m *memberlist) String() string {
	m.members.RLock()
	str, _ := json.Marshal(m.members.list) // will never return error (presumably)
	m.members.RUnlock()
	return string(str)
}

// Iter returns a MemberlistIter for the Memberlist
func (m *memberlist) Iter() *memberlistIter {
	return newMemberlistIter(m)
}

func (m *memberlist) GetReachableMembers() []string {
	var active []string

	m.members.RLock()
	for _, member := range m.members.list {
		if member.isReachable() {
			active = append(active, member.Address)
		}
	}
	m.members.RUnlock()

	return active
}

func (m *memberlist) CountReachableMembers() int {
	count := 0

	m.members.RLock()
	for _, member := range m.members.list {
		if member.isReachable() {
			count++
		}
	}
	m.members.RUnlock()

	return count
}

// nowInMillis is a utility function that call Now on the clock and converts it
// to milliseconds.
func nowInMillis(c clock.Clock) int64 {
	return c.Now().UnixNano() / int64(time.Millisecond)
}
<commit_msg>Fix race in membership and ring consistency<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package swim

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math/rand"
	"sort"
	"sync"
	"time"

	"github.com/benbjohnson/clock"
	"github.com/dgryski/go-farm"
	"github.com/uber/ringpop-go/util"
)

// A memberlist contains the membership for a node
type memberlist struct {
	node  *Node
	local *Member

	members struct {
		list      []*Member
		byAddress map[string]*Member
		checksum  uint32
		sync.RWMutex
	}

	// TODO: rework locking in ringpop-go (see #113). Required for Update().

	// Updates to membership list and hash ring should happen atomically. We
	// could use members lock for that, but that introduces more deadlocks, so
	// making a short-term fix instead by adding another lock. Like said, this
	// is short-term, see github#113.
	sync.Mutex
}

// newMemberlist returns a new member list
func newMemberlist(n *Node) *memberlist {
	m := &memberlist{
		node: n,
	}

	m.members.byAddress = make(map[string]*Member)

	return m
}

func (m *memberlist) Checksum() uint32 {
	m.members.Lock()
	checksum := m.members.checksum
	m.members.Unlock()

	return checksum
}

// computes membership checksum
func (m *memberlist) ComputeChecksum() {
	startTime := time.Now()
	m.members.Lock()
	checksum := farm.Fingerprint32([]byte(m.GenChecksumString()))
	m.members.checksum = checksum
	m.members.Unlock()
	m.node.emit(ChecksumComputeEvent{
		Duration: time.Now().Sub(startTime),
		Checksum: checksum,
	})
}

// generates string to use when computing checksum
func (m *memberlist) GenChecksumString() string {
	var strings sort.StringSlice

	for _, member := range m.members.list {
		s := fmt.Sprintf("%s%s%v", member.Address, member.Status, member.Incarnation)
		strings = append(strings, s)
	}

	strings.Sort()

	buffer := bytes.NewBuffer([]byte{})
	for _, str := range strings {
		buffer.WriteString(str)
		buffer.WriteString(";")
	}

	return buffer.String()
}

// returns the member at a specific address
func (m *memberlist) Member(address string) (*Member, bool) {
	m.members.RLock()
	member, ok := m.members.byAddress[address]
	m.members.RUnlock()

	return member, ok
}

func (m *memberlist) MemberAt(i int) *Member {
	m.members.RLock()
	member := m.members.list[i]
	m.members.RUnlock()

	return member
}

func (m *memberlist) NumMembers() int {
	m.members.RLock()
	n := len(m.members.list)
	m.members.RUnlock()

	return n
}

// returns whether or not a member is pingable
func (m *memberlist) Pingable(member Member) bool {
	return member.Address != m.local.Address &&
		(member.Status == Alive || member.Status == Suspect)

}

// returns the number of pingable members in the memberlist
func (m *memberlist) NumPingableMembers() (n int) {
	m.members.Lock()
	for _, member := range m.members.list {
		if m.Pingable(*member) {
			n++
		}
	}
	m.members.Unlock()

	return n
}

// returns n pingable members in the member list
func (m *memberlist) RandomPingableMembers(n int, excluding map[string]bool) []*Member {
	var members []*Member

	m.members.RLock()
	for _, member := range m.members.list {
		if m.Pingable(*member) && !excluding[member.Address] {
			members = append(members, member)
		}
	}
	m.members.RUnlock()

	// shuffle members and take first n
	members = shuffle(members)

	if n > len(members) {
		return members
	}
	return members[:n]
}

// returns an immutable slice of members representing the current state of the membership
func (m *memberlist) GetMembers() (members []Member) {
	m.members.RLock()
	for _, member := range m.members.list {
		members = append(members, *member)
	}
	m.members.RUnlock()

	return
}

// Reincarnate sets the status of the node to Alive and updates the incarnation
// number. It adds the change to the disseminator as well.
func (m *memberlist) Reincarnate() []Change {
	return m.MakeAlive(m.node.address, nowInMillis(m.node.clock))
}

func (m *memberlist) MakeAlive(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Alive})
	return m.MakeChange(address, incarnation, Alive)
}

func (m *memberlist) MakeSuspect(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Suspect})
	return m.MakeChange(address, incarnation, Suspect)
}

func (m *memberlist) MakeFaulty(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Faulty})
	return m.MakeChange(address, incarnation, Faulty)
}

func (m *memberlist) MakeLeave(address string, incarnation int64) []Change {
	m.node.emit(MakeNodeStatusEvent{Leave})
	return m.MakeChange(address, incarnation, Leave)
}

// makes a change to the member list
func (m *memberlist) MakeChange(address string, incarnation int64, status string) []Change {
	if m.local == nil {
		m.local = &Member{
			Address:     m.node.Address(),
			Incarnation: util.TimeNowMS(),
			Status:      Alive,
		}
	}

	return m.Update([]Change{Change{
		Source:            m.local.Address,
		SourceIncarnation: m.local.Incarnation,
		Address:           address,
		Incarnation:       incarnation,
		Status:            status,
		Timestamp:         util.Timestamp(time.Now()),
	}})
}

// updates the member list with the slice of changes, applying selectively
func (m *memberlist) Update(changes []Change) (applied []Change) {
	if m.node.Stopped() || len(changes) == 0 {
		return nil
	}

	m.node.emit(MemberlistChangesReceivedEvent{changes})

	m.Lock()
	m.members.Lock()

	for _, change := range changes {
		member, ok := m.members.byAddress[change.Address]

		// first time member has been seen, take change wholesale
		if !ok {
			m.Apply(change)
			applied = append(applied, change)
			continue
		}

		// if change is local override, reassert member is alive
		if member.localOverride(m.node.Address(), change) {
			m.node.emit(RefuteUpdateEvent{})
			overrideChange := Change{
				Source:            change.Source,
				SourceIncarnation: change.SourceIncarnation,
				Address:           change.Address,
				Incarnation:       nowInMillis(m.node.clock),
				Status:            Alive,
				Timestamp:         util.Timestamp(time.Now()),
			}

			m.Apply(overrideChange)
			applied = append(applied, overrideChange)
			continue
		}

		// if non-local override, apply change wholesale
		if member.nonLocalOverride(change) {
			m.Apply(change)
			applied = append(applied, change)
		}
	}

	m.members.Unlock()

	if len(applied) > 0 {
		oldChecksum := m.Checksum()
		m.ComputeChecksum()
		m.node.emit(MemberlistChangesAppliedEvent{
			Changes:     applied,
			OldChecksum: oldChecksum,
			NewChecksum: m.Checksum(),
			NumMembers:  m.NumMembers(),
		})
		m.node.handleChanges(applied)
		m.node.rollup.TrackUpdates(applied)
	}

	m.Unlock()
	return applied
}

// AddJoinList adds the list to the membership with the Update
// function. However, as a side effect, Update adds changes to
// the disseminator as well. Since we don't want to disseminate
// the potentially very large join lists, we clear all the
// changes from the disseminator, except for the one change
// that refers to the make-alive of this node.
func (m *memberlist) AddJoinList(list []Change) {
	applied := m.Update(list)
	for _, member := range applied {
		if member.Address == m.node.Address() {
			continue
		}
		m.node.disseminator.ClearChange(member.Address)
	}
}

// gets a random position in [0, length of member list)
func (m *memberlist) getJoinPosition() int {
	l := len(m.members.list)
	if l == 0 {
		return l
	}
	return rand.Intn(l)
}

// applies a change directly to the member list
func (m *memberlist) Apply(change Change) {
	member, ok := m.members.byAddress[change.Address]

	if !ok {
		member = &Member{
			Address:     change.Address,
			Status:      change.Status,
			Incarnation: change.Incarnation,
		}

		if member.Address == m.node.Address() {
			m.local = member
		}

		m.members.byAddress[change.Address] = member
		i := m.getJoinPosition()
		m.members.list = append(m.members.list[:i], append([]*Member{member}, m.members.list[i:]...)...)
	}

	member.Lock()
	member.Status = change.Status
	member.Incarnation = change.Incarnation
	member.Unlock()
}

// shuffles the member list
func (m *memberlist) Shuffle() {
	m.members.Lock()
	m.members.list = shuffle(m.members.list)
	m.members.Unlock()
}

// String returns a JSON string
func (m *memberlist) String() string {
	m.members.RLock()
	str, _ := json.Marshal(m.members.list) // will never return error (presumably)
	m.members.RUnlock()
	return string(str)
}

// Iter returns a MemberlistIter for the Memberlist
func (m *memberlist) Iter() *memberlistIter {
	return newMemberlistIter(m)
}

func (m *memberlist) GetReachableMembers() []string {
	var active []string

	m.members.RLock()
	for _, member := range m.members.list {
		if member.isReachable() {
			active = append(active, member.Address)
		}
	}
	m.members.RUnlock()

	return active
}

func (m *memberlist) CountReachableMembers() int {
	count := 0

	m.members.RLock()
	for _, member := range m.members.list {
		if member.isReachable() {
			count++
		}
	}
	m.members.RUnlock()

	return count
}

// nowInMillis is a utility function that call Now on the clock and converts it
// to milliseconds.
func nowInMillis(c clock.Clock) int64 {
	return c.Now().UnixNano() / int64(time.Millisecond)
}
<|endoftext|>


<commit_before>package endpoints

import (
	"bytes"
	"context"
	"sort"

	"encoding/json"
	"fmt"
	"reflect"

	"net"

	workloadUtil "github.com/rancher/rancher/pkg/controllers/user/workload"
	nodehelper "github.com/rancher/rancher/pkg/node"
	"github.com/rancher/types/apis/core/v1"
	managementv3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
)

const (
	allEndpoints        = "_all_endpoints_"
	endpointsAnnotation = "field.cattle.io/publicEndpoints"
)

func Register(ctx context.Context, workload *config.UserContext) {
	isRKE := false
	cluster, err := workload.Management.Management.Clusters("").Get(workload.ClusterName, metav1.GetOptions{})
	if err != nil {
		logrus.WithError(err).Warnf("Can not get cluster %s when registering endpoint controller", workload.ClusterName)
	}
	if cluster != nil {
		//assume that cluster always has a spec
		isRKE = cluster.Spec.RancherKubernetesEngineConfig != nil
	}
	n := &NodesController{
		nodes:              workload.Core.Nodes(""),
		serviceLister:      workload.Core.Services("").Controller().Lister(),
		nodeLister:         workload.Core.Nodes("").Controller().Lister(),
		podLister:          workload.Core.Pods("").Controller().Lister(),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		clusterName:        workload.ClusterName,
	}
	workload.Core.Nodes("").AddHandler("nodesEndpointsController", n.sync)

	s := &ServicesController{
		services:           workload.Core.Services(""),
		serviceLister:      workload.Core.Services("").Controller().Lister(),
		nodeLister:         workload.Core.Nodes("").Controller().Lister(),
		nodeController:     workload.Core.Nodes("").Controller(),
		podLister:          workload.Core.Pods("").Controller().Lister(),
		podController:      workload.Core.Pods("").Controller(),
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		clusterName:        workload.ClusterName,
	}
	workload.Core.Services("").AddHandler("servicesEndpointsController", s.sync)

	p := &PodsController{
		nodeLister:         workload.Core.Nodes("").Controller().Lister(),
		nodeController:     workload.Core.Nodes("").Controller(),
		pods:               workload.Core.Pods(""),
		serviceLister:      workload.Core.Services("").Controller().Lister(),
		podLister:          workload.Core.Pods("").Controller().Lister(),
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		clusterName:        workload.ClusterName,
	}
	workload.Core.Pods("").AddHandler("hostPortEndpointsController", p.sync)

	w := &WorkloadEndpointsController{
		ingressLister:  workload.Extensions.Ingresses("").Controller().Lister(),
		serviceLister:  workload.Core.Services("").Controller().Lister(),
		podLister:      workload.Core.Pods("").Controller().Lister(),
		machinesLister: workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		nodeLister:     workload.Core.Nodes("").Controller().Lister(),
		clusterName:    workload.ClusterName,
		isRKE:          isRKE,
	}
	w.WorkloadController = workloadUtil.NewWorkloadController(workload.UserOnlyContext(), w.UpdateEndpoints)

	i := &IngressEndpointsController{
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		ingressInterface:   workload.Extensions.Ingresses(""),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		isRKE:              isRKE,
		clusterName:        workload.ClusterName,
	}
	workload.Extensions.Ingresses("").AddHandler("ingressEndpointsController", i.sync)
}

func areEqualEndpoints(one []v3.PublicEndpoint, two []v3.PublicEndpoint) bool {
	oneMap := map[string]bool{}
	twoMap := map[string]bool{}
	for _, value := range one {
		oneMap[publicEndpointToString(value)] = true
	}
	for _, value := range two {
		twoMap[publicEndpointToString(value)] = true
	}
	return reflect.DeepEqual(oneMap, twoMap)
}

func publicEndpointsToString(eps []v3.PublicEndpoint) (string, error) {
	b, err := json.Marshal(eps)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func getPublicEndpointsFromAnnotations(annotations map[string]string) []v3.PublicEndpoint {
	var eps []v3.PublicEndpoint
	if annotations == nil {
		return eps
	}
	if val, ok := annotations[endpointsAnnotation]; ok {
		err := json.Unmarshal([]byte(val), &eps)
		if err != nil {
			logrus.Errorf("Failed to read public endpoints from annotation %v", err)
			return eps
		}
	}
	return eps
}

func convertServiceToPublicEndpoints(svc *corev1.Service, clusterName string, node *managementv3.Node, allNodesIP string) ([]v3.PublicEndpoint, error) {
	var eps []v3.PublicEndpoint
	if svc.DeletionTimestamp != nil {
		return eps, nil
	}
	if !(svc.Spec.Type == "NodePort" || svc.Spec.Type == "LoadBalancer") {
		return eps, nil
	}
	address := ""
	nodeName := ""
	if node != nil {
		address = nodehelper.GetEndpointNodeIP(node)
		nodeName = fmt.Sprintf("%s:%s", clusterName, node.Name)
	}

	svcName := fmt.Sprintf("%s:%s", svc.Namespace, svc.Name)
	if svc.Spec.Type == "NodePort" {
		addresses := []string{}
		if allNodesIP != "" {
			addresses = append(addresses, allNodesIP)
		}
		for _, port := range svc.Spec.Ports {
			if port.NodePort == 0 {
				continue
			}
			p := v3.PublicEndpoint{
				NodeName:    nodeName,
				Port:        port.NodePort,
				Addresses:   addresses,
				Protocol:    string(port.Protocol),
				ServiceName: svcName,
				AllNodes:    true,
			}
			//for getting endpoints of specific node
			if address != "" {
				p.Addresses = append(p.Addresses, address)
			}
			eps = append(eps, p)
		}
	} else {
		var addresses []string
		for _, ingressEp := range svc.Status.LoadBalancer.Ingress {
			address := ingressEp.Hostname
			if address == "" {
				address = ingressEp.IP
			}
			addresses = append(addresses, address)
		}
		if len(addresses) > 0 {
			for _, port := range svc.Spec.Ports {
				p := v3.PublicEndpoint{
					NodeName:    "",
					Addresses:   addresses,
					Port:        port.Port,
					Protocol:    string(port.Protocol),
					ServiceName: svcName,
					AllNodes:    false,
				}
				eps = append(eps, p)
			}
		}
	}

	return eps, nil
}

func convertHostPortToEndpoint(pod *corev1.Pod, clusterName string, node *managementv3.Node) ([]v3.PublicEndpoint, error) {
	var eps []v3.PublicEndpoint
	if pod.DeletionTimestamp != nil {
		return eps, nil
	}
	if pod.Status.Phase != corev1.PodRunning {
		return eps, nil
	}
	if node == nil {
		return eps, nil
	}
	for _, c := range pod.Spec.Containers {
		for _, p := range c.Ports {
			if p.HostPort == 0 {
				continue
			}
			address := nodehelper.GetEndpointNodeIP(node)
			p := v3.PublicEndpoint{
				NodeName:  fmt.Sprintf("%s:%s", clusterName, node.Name),
				Addresses: []string{address},
				Port:      p.HostPort,
				Protocol:  string(p.Protocol),
				PodName:   fmt.Sprintf("%s:%s", pod.Namespace, pod.Name),
				AllNodes:  false,
			}
			eps = append(eps, p)
		}
	}

	return eps, nil
}

func publicEndpointToString(p v3.PublicEndpoint) string {
	sort.Strings(p.Addresses)
	return fmt.Sprintf("%s_%v_%v_%s_%s_%s_%s_%s_%s", p.NodeName, p.Addresses, p.Port, p.Protocol, p.ServiceName, p.PodName, p.IngressName, p.Hostname, p.Path)
}

func getNodeNameToMachine(clusterName string, machineLister managementv3.NodeLister, nodeLister v1.NodeLister) (map[string]*managementv3.Node, error) {
	machines, err := machineLister.List(clusterName, labels.NewSelector())
	if err != nil {
		return nil, err
	}
	machineMap := map[string]*managementv3.Node{}
	nodes, err := nodeLister.List("", labels.NewSelector())
	if err != nil {
		return nil, err
	}
	for _, machine := range machines {
		for _, node := range nodes {
			if nodehelper.IsNodeForNode(node, machine) {
				machineMap[node.Name] = machine
			}
		}
	}
	return machineMap, nil
}

func isMachineReady(machine *managementv3.Node) bool {
	for _, cond := range machine.Status.InternalNodeStatus.Conditions {
		if cond.Type == corev1.NodeReady {
			return cond.Status == corev1.ConditionTrue
		}
	}
	return false
}

func getAllNodesPublicEndpointIP(machineLister managementv3.NodeLister, clusterName string) (string, error) {
	var addresses []net.IP
	machines, err := machineLister.List(clusterName, labels.NewSelector())
	if err != nil {
		return "", err
	}
	for _, machine := range machines {
		if machine.Spec.Worker && isMachineReady(machine) {
			nodePublicIP := net.ParseIP(nodehelper.GetEndpointNodeIP(machine))
			if nodePublicIP.String() != "" {
				addresses = append(addresses, nodePublicIP)
			}
		}
	}
	if len(addresses) == 0 {
		return "", nil
	}

	sort.Slice(addresses, func(i, j int) bool {
		return bytes.Compare(addresses[i], addresses[j]) < 0
	})

	return addresses[0].String(), nil
}

func convertIngressToServicePublicEndpointsMap(obj *extensionsv1beta1.Ingress, allNodes bool, allNodesIP string) map[string][]v3.PublicEndpoint {
	var addresses []string
	epsMap := map[string][]v3.PublicEndpoint{}
	if !allNodes {
		for _, address := range obj.Status.LoadBalancer.Ingress {
			addresses = append(addresses, address.IP)
		}

	} else {
		if allNodesIP != "" {
			addresses = append(addresses, allNodesIP)
		}
	}

	if len(addresses) == 0 {
		return epsMap
	}

	ports := map[int32]string{80: "HTTP", 443: "HTTPS"}
	for _, rule := range obj.Spec.Rules {
		for _, path := range rule.HTTP.Paths {
			for port, proto := range ports {
				p := v3.PublicEndpoint{
					Hostname:    rule.Host,
					Path:        path.Path,
					ServiceName: fmt.Sprintf("%s:%s", obj.Namespace, path.Backend.ServiceName),
					Addresses:   addresses,
					Port:        port,
					Protocol:    proto,
					AllNodes:    allNodes,
					IngressName: fmt.Sprintf("%s:%s", obj.Namespace, obj.Name),
				}
				epsMap[path.Backend.ServiceName] = append(epsMap[path.Backend.ServiceName], p)
			}
		}
	}
	return epsMap
}

func convertIngressToPublicEndpoints(obj *extensionsv1beta1.Ingress, isRKE bool, allNodesIP string) []v3.PublicEndpoint {
	epsMap := convertIngressToServicePublicEndpointsMap(obj, isRKE, allNodesIP)
	var eps []v3.PublicEndpoint
	for _, v := range epsMap {
		eps = append(eps, v...)
	}
	return eps
}
<commit_msg>Shows ingress endpoint with rules<commit_after>package endpoints

import (
	"bytes"
	"context"
	"sort"

	"encoding/json"
	"fmt"
	"reflect"

	"net"

	workloadUtil "github.com/rancher/rancher/pkg/controllers/user/workload"
	nodehelper "github.com/rancher/rancher/pkg/node"
	"github.com/rancher/types/apis/core/v1"
	managementv3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	allEndpoints        = "_all_endpoints_"
	endpointsAnnotation = "field.cattle.io/publicEndpoints"
)

func Register(ctx context.Context, workload *config.UserContext) {
	isRKE := false
	cluster, err := workload.Management.Management.Clusters("").Get(workload.ClusterName, metav1.GetOptions{})
	if err != nil {
		logrus.WithError(err).Warnf("Can not get cluster %s when registering endpoint controller", workload.ClusterName)
	}
	if cluster != nil {
		//assume that cluster always has a spec
		isRKE = cluster.Spec.RancherKubernetesEngineConfig != nil
	}
	n := &NodesController{
		nodes:              workload.Core.Nodes(""),
		serviceLister:      workload.Core.Services("").Controller().Lister(),
		nodeLister:         workload.Core.Nodes("").Controller().Lister(),
		podLister:          workload.Core.Pods("").Controller().Lister(),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		clusterName:        workload.ClusterName,
	}
	workload.Core.Nodes("").AddHandler("nodesEndpointsController", n.sync)

	s := &ServicesController{
		services:           workload.Core.Services(""),
		serviceLister:      workload.Core.Services("").Controller().Lister(),
		nodeLister:         workload.Core.Nodes("").Controller().Lister(),
		nodeController:     workload.Core.Nodes("").Controller(),
		podLister:          workload.Core.Pods("").Controller().Lister(),
		podController:      workload.Core.Pods("").Controller(),
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		clusterName:        workload.ClusterName,
	}
	workload.Core.Services("").AddHandler("servicesEndpointsController", s.sync)

	p := &PodsController{
		nodeLister:         workload.Core.Nodes("").Controller().Lister(),
		nodeController:     workload.Core.Nodes("").Controller(),
		pods:               workload.Core.Pods(""),
		serviceLister:      workload.Core.Services("").Controller().Lister(),
		podLister:          workload.Core.Pods("").Controller().Lister(),
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		clusterName:        workload.ClusterName,
	}
	workload.Core.Pods("").AddHandler("hostPortEndpointsController", p.sync)

	w := &WorkloadEndpointsController{
		ingressLister:  workload.Extensions.Ingresses("").Controller().Lister(),
		serviceLister:  workload.Core.Services("").Controller().Lister(),
		podLister:      workload.Core.Pods("").Controller().Lister(),
		machinesLister: workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		nodeLister:     workload.Core.Nodes("").Controller().Lister(),
		clusterName:    workload.ClusterName,
		isRKE:          isRKE,
	}
	w.WorkloadController = workloadUtil.NewWorkloadController(workload.UserOnlyContext(), w.UpdateEndpoints)

	i := &IngressEndpointsController{
		workloadController: workloadUtil.NewWorkloadController(workload.UserOnlyContext(), nil),
		ingressInterface:   workload.Extensions.Ingresses(""),
		machinesLister:     workload.Management.Management.Nodes(workload.ClusterName).Controller().Lister(),
		isRKE:              isRKE,
		clusterName:        workload.ClusterName,
	}
	workload.Extensions.Ingresses("").AddHandler("ingressEndpointsController", i.sync)
}

func areEqualEndpoints(one []v3.PublicEndpoint, two []v3.PublicEndpoint) bool {
	oneMap := map[string]bool{}
	twoMap := map[string]bool{}
	for _, value := range one {
		oneMap[publicEndpointToString(value)] = true
	}
	for _, value := range two {
		twoMap[publicEndpointToString(value)] = true
	}
	return reflect.DeepEqual(oneMap, twoMap)
}

func publicEndpointsToString(eps []v3.PublicEndpoint) (string, error) {
	b, err := json.Marshal(eps)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func getPublicEndpointsFromAnnotations(annotations map[string]string) []v3.PublicEndpoint {
	var eps []v3.PublicEndpoint
	if annotations == nil {
		return eps
	}
	if val, ok := annotations[endpointsAnnotation]; ok {
		err := json.Unmarshal([]byte(val), &eps)
		if err != nil {
			logrus.Errorf("Failed to read public endpoints from annotation %v", err)
			return eps
		}
	}
	return eps
}

func convertServiceToPublicEndpoints(svc *corev1.Service, clusterName string, node *managementv3.Node, allNodesIP string) ([]v3.PublicEndpoint, error) {
	var eps []v3.PublicEndpoint
	if svc.DeletionTimestamp != nil {
		return eps, nil
	}
	if !(svc.Spec.Type == "NodePort" || svc.Spec.Type == "LoadBalancer") {
		return eps, nil
	}
	address := ""
	nodeName := ""
	if node != nil {
		address = nodehelper.GetEndpointNodeIP(node)
		nodeName = fmt.Sprintf("%s:%s", clusterName, node.Name)
	}

	svcName := fmt.Sprintf("%s:%s", svc.Namespace, svc.Name)
	if svc.Spec.Type == "NodePort" {
		addresses := []string{}
		if allNodesIP != "" {
			addresses = append(addresses, allNodesIP)
		}
		for _, port := range svc.Spec.Ports {
			if port.NodePort == 0 {
				continue
			}
			p := v3.PublicEndpoint{
				NodeName:    nodeName,
				Port:        port.NodePort,
				Addresses:   addresses,
				Protocol:    string(port.Protocol),
				ServiceName: svcName,
				AllNodes:    true,
			}
			//for getting endpoints of specific node
			if address != "" {
				p.Addresses = append(p.Addresses, address)
			}
			eps = append(eps, p)
		}
	} else {
		var addresses []string
		for _, ingressEp := range svc.Status.LoadBalancer.Ingress {
			address := ingressEp.Hostname
			if address == "" {
				address = ingressEp.IP
			}
			addresses = append(addresses, address)
		}
		if len(addresses) > 0 {
			for _, port := range svc.Spec.Ports {
				p := v3.PublicEndpoint{
					NodeName:    "",
					Addresses:   addresses,
					Port:        port.Port,
					Protocol:    string(port.Protocol),
					ServiceName: svcName,
					AllNodes:    false,
				}
				eps = append(eps, p)
			}
		}
	}

	return eps, nil
}

func convertHostPortToEndpoint(pod *corev1.Pod, clusterName string, node *managementv3.Node) ([]v3.PublicEndpoint, error) {
	var eps []v3.PublicEndpoint
	if pod.DeletionTimestamp != nil {
		return eps, nil
	}
	if pod.Status.Phase != corev1.PodRunning {
		return eps, nil
	}
	if node == nil {
		return eps, nil
	}
	for _, c := range pod.Spec.Containers {
		for _, p := range c.Ports {
			if p.HostPort == 0 {
				continue
			}
			address := nodehelper.GetEndpointNodeIP(node)
			p := v3.PublicEndpoint{
				NodeName:  fmt.Sprintf("%s:%s", clusterName, node.Name),
				Addresses: []string{address},
				Port:      p.HostPort,
				Protocol:  string(p.Protocol),
				PodName:   fmt.Sprintf("%s:%s", pod.Namespace, pod.Name),
				AllNodes:  false,
			}
			eps = append(eps, p)
		}
	}

	return eps, nil
}

func publicEndpointToString(p v3.PublicEndpoint) string {
	sort.Strings(p.Addresses)
	return fmt.Sprintf("%s_%v_%v_%s_%s_%s_%s_%s_%s", p.NodeName, p.Addresses, p.Port, p.Protocol, p.ServiceName, p.PodName, p.IngressName, p.Hostname, p.Path)
}

func getNodeNameToMachine(clusterName string, machineLister managementv3.NodeLister, nodeLister v1.NodeLister) (map[string]*managementv3.Node, error) {
	machines, err := machineLister.List(clusterName, labels.NewSelector())
	if err != nil {
		return nil, err
	}
	machineMap := map[string]*managementv3.Node{}
	nodes, err := nodeLister.List("", labels.NewSelector())
	if err != nil {
		return nil, err
	}
	for _, machine := range machines {
		for _, node := range nodes {
			if nodehelper.IsNodeForNode(node, machine) {
				machineMap[node.Name] = machine
			}
		}
	}
	return machineMap, nil
}

func isMachineReady(machine *managementv3.Node) bool {
	for _, cond := range machine.Status.InternalNodeStatus.Conditions {
		if cond.Type == corev1.NodeReady {
			return cond.Status == corev1.ConditionTrue
		}
	}
	return false
}

func getAllNodesPublicEndpointIP(machineLister managementv3.NodeLister, clusterName string) (string, error) {
	var addresses []net.IP
	machines, err := machineLister.List(clusterName, labels.NewSelector())
	if err != nil {
		return "", err
	}
	for _, machine := range machines {
		if machine.Spec.Worker && isMachineReady(machine) {
			nodePublicIP := net.ParseIP(nodehelper.GetEndpointNodeIP(machine))
			if nodePublicIP.String() != "" {
				addresses = append(addresses, nodePublicIP)
			}
		}
	}
	if len(addresses) == 0 {
		return "", nil
	}

	sort.Slice(addresses, func(i, j int) bool {
		return bytes.Compare(addresses[i], addresses[j]) < 0
	})

	return addresses[0].String(), nil
}

func convertIngressToServicePublicEndpointsMap(obj *extensionsv1beta1.Ingress, allNodes bool, allNodesIP string) map[string][]v3.PublicEndpoint {
	var addresses []string
	epsMap := map[string][]v3.PublicEndpoint{}
	if !allNodes {
		for _, address := range obj.Status.LoadBalancer.Ingress {
			addresses = append(addresses, address.IP)
		}

	} else {
		if allNodesIP != "" {
			addresses = append(addresses, allNodesIP)
		}
	}

	if len(addresses) == 0 {
		return epsMap
	}

	tlsHosts := sets.NewString()
	for _, t := range obj.Spec.TLS {
		tlsHosts.Insert(t.Hosts...)
	}

	ports := map[int32]string{80: "HTTP", 443: "HTTPS"}
	for _, rule := range obj.Spec.Rules {
		for _, path := range rule.HTTP.Paths {
			for port, proto := range ports {
				if port == 80 {
					if tlsHosts.Has(rule.Host) {
						continue
					}
				} else {
					if !tlsHosts.Has(rule.Host) && rule.Host != "" {
						continue
					}
				}
				p := v3.PublicEndpoint{
					Hostname:    rule.Host,
					Path:        path.Path,
					ServiceName: fmt.Sprintf("%s:%s", obj.Namespace, path.Backend.ServiceName),
					Addresses:   addresses,
					Port:        port,
					Protocol:    proto,
					AllNodes:    allNodes,
					IngressName: fmt.Sprintf("%s:%s", obj.Namespace, obj.Name),
				}
				epsMap[path.Backend.ServiceName] = append(epsMap[path.Backend.ServiceName], p)
			}
		}
	}
	return epsMap
}

func convertIngressToPublicEndpoints(obj *extensionsv1beta1.Ingress, isRKE bool, allNodesIP string) []v3.PublicEndpoint {
	epsMap := convertIngressToServicePublicEndpointsMap(obj, isRKE, allNodesIP)
	var eps []v3.PublicEndpoint
	for _, v := range epsMap {
		eps = append(eps, v...)
	}
	return eps
}
<|endoftext|>


<commit_before>package payment

import (
	"code.google.com/p/godec/dec"
	"database/sql"
	"database/sql/driver"
	"fmt"
	"github.com/fritzpay/paymentd/pkg/decimal"
	"time"
)

type PaymentTransactionStatus string

// Scan implements the Scanner interface for sql
func (s *PaymentTransactionStatus) Scan(v interface{}) error {
	switch src := v.(type) {
	case []byte:
		*s = PaymentTransactionStatus(string(src))
		return nil
	case nil:
		*s = PaymentStatusNone
		return nil
	}
	str, ok := v.(string)
	if !ok {
		return fmt.Errorf("cannot scan %T into %T", v, s)
	}
	*s = PaymentTransactionStatus(str)
	return nil
}

// Value implements the Valuer interface for sql
func (s PaymentTransactionStatus) Value() (driver.Value, error) {
	return driver.Value(s.String()), nil
}

func (s PaymentTransactionStatus) Valid() bool {
	return string(s) != ""
}

func (s PaymentTransactionStatus) String() string {
	return string(s)
}

const (
	PaymentStatusNone           PaymentTransactionStatus = "uninitialized"
	PaymentStatusOpen                                    = "open"
	PaymentStatusPending                                 = "pending"
	PaymentStatusPaid                                    = "paid"
	PaymentStatusSettled                                 = "settled"
	PaymentStatusAuthorized                              = "authorized"
	PaymentStatusError                                   = "error"
	PaymentStatusCancelled                               = "cancelled"
	PaymentStatusFailed                                  = "failed"
	PaymentStatusChargeback                              = "chargeback"
	PaymentStatusRefunded                                = "refunded"
	PaymentStatusRefundReversed                          = "refund-reversed"
)

// PaymentTransaction represents a transaction on a payment
//
// A transaction is any event/status change on a payment
//
// The transactions represents the ledger on a payment
type PaymentTransaction struct {
	Payment *Payment

	Timestamp time.Time
	Amount    int64
	Subunits  int8
	Currency  string
	Status    PaymentTransactionStatus
	Comment   sql.NullString
}

func (p *PaymentTransaction) Decimal() *decimal.Decimal {
	d := dec.NewDecInt64(p.Amount)
	sc := dec.Scale(int32(p.Subunits))
	d.SetScale(sc)
	return &decimal.Decimal{Dec: *d}
}

// Balance represents a balance which totals the ledger by currency
type Balance map[string]*decimal.Decimal

func (b Balance) FlatMap() map[string]string {
	flat := make(map[string]string)
	for curr, dec := range b {
		flat[curr] = dec.String()
	}
	return flat
}

type PaymentTransactionList []*PaymentTransaction

func (p PaymentTransactionList) Balance() Balance {
	b := make(map[string]*decimal.Decimal)
	for _, tx := range p {
		am := tx.Decimal()
		if _, ok := b[tx.Currency]; ok {
			b[tx.Currency].Add(&b[tx.Currency].Dec, &am.Dec)
		} else {
			b[tx.Currency] = am
		}
	}
	return b
}
<commit_msg>correctly encode balance in JSON<commit_after>package payment

import (
	"code.google.com/p/godec/dec"
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"github.com/fritzpay/paymentd/pkg/decimal"
	"time"
)

type PaymentTransactionStatus string

// Scan implements the Scanner interface for sql
func (s *PaymentTransactionStatus) Scan(v interface{}) error {
	switch src := v.(type) {
	case []byte:
		*s = PaymentTransactionStatus(string(src))
		return nil
	case nil:
		*s = PaymentStatusNone
		return nil
	}
	str, ok := v.(string)
	if !ok {
		return fmt.Errorf("cannot scan %T into %T", v, s)
	}
	*s = PaymentTransactionStatus(str)
	return nil
}

// Value implements the Valuer interface for sql
func (s PaymentTransactionStatus) Value() (driver.Value, error) {
	return driver.Value(s.String()), nil
}

func (s PaymentTransactionStatus) Valid() bool {
	return string(s) != ""
}

func (s PaymentTransactionStatus) String() string {
	return string(s)
}

const (
	PaymentStatusNone           PaymentTransactionStatus = "uninitialized"
	PaymentStatusOpen                                    = "open"
	PaymentStatusPending                                 = "pending"
	PaymentStatusPaid                                    = "paid"
	PaymentStatusSettled                                 = "settled"
	PaymentStatusAuthorized                              = "authorized"
	PaymentStatusError                                   = "error"
	PaymentStatusCancelled                               = "cancelled"
	PaymentStatusFailed                                  = "failed"
	PaymentStatusChargeback                              = "chargeback"
	PaymentStatusRefunded                                = "refunded"
	PaymentStatusRefundReversed                          = "refund-reversed"
)

// PaymentTransaction represents a transaction on a payment
//
// A transaction is any event/status change on a payment
//
// The transactions represents the ledger on a payment
type PaymentTransaction struct {
	Payment *Payment

	Timestamp time.Time
	Amount    int64
	Subunits  int8
	Currency  string
	Status    PaymentTransactionStatus
	Comment   sql.NullString
}

func (p *PaymentTransaction) Decimal() *decimal.Decimal {
	d := dec.NewDecInt64(p.Amount)
	sc := dec.Scale(int32(p.Subunits))
	d.SetScale(sc)
	return &decimal.Decimal{Dec: *d}
}

// Balance represents a balance which totals the ledger by currency
type Balance map[string]*decimal.Decimal

func (b Balance) FlatMap() map[string]string {
	flat := make(map[string]string)
	for curr, dec := range b {
		flat[curr] = dec.String()
	}
	return flat
}

func (b Balance) MarshalJSON() ([]byte, error) {
	return json.Marshal(b.FlatMap())
}

type PaymentTransactionList []*PaymentTransaction

func (p PaymentTransactionList) Balance() Balance {
	b := make(map[string]*decimal.Decimal)
	for _, tx := range p {
		am := tx.Decimal()
		if _, ok := b[tx.Currency]; ok {
			b[tx.Currency].Add(&b[tx.Currency].Dec, &am.Dec)
		} else {
			b[tx.Currency] = am
		}
	}
	return b
}
<|endoftext|>


<commit_before>package scraper

import (
	"bytes"
	"fmt"
	"net/url"
	"path"
	"regexp"
	"strings"

	"github.com/gorilla/css/scanner"
	"github.com/headzoo/surf/browser"
	"go.uber.org/zap"
)

var cssURLRe = regexp.MustCompile(`^url\(['"]?(.*?)['"]?\)$`)

func (s *Scraper) checkCSSForURLs(URL *url.URL, buf *bytes.Buffer) *bytes.Buffer {
	replacings := make(map[string]string)
	str := buf.String()
	css := scanner.New(str)

	for {
		token := css.Next()
		if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
			break
		}

		if token.Type != scanner.TokenURI {
			continue
		}

		match := cssURLRe.FindStringSubmatch(token.Value)
		if match == nil {
			continue
		}

		src := match[1]
		if strings.HasPrefix(strings.ToLower(src), "data:") {
			continue // skip embedded data
		}

		u, err := url.Parse(src)
		if err != nil {
			return buf
		}
		u = URL.ResolveReference(u)

		cssPath := *URL
		cssPath.Path = path.Dir(cssPath.Path) + "/"

		img := browser.NewImageAsset(u, "", "", "")
		s.imagesQueue = append(s.imagesQueue, &img.DownloadableAsset)

		resolved := s.resolveURL(&cssPath, src, false, "")
		replacings[token.Value] = resolved
	}

	if len(replacings) == 0 {
		return buf
	}

	for ori, fpath := range replacings {
		fixed := fmt.Sprintf("url(%s)", fpath)
		str = strings.Replace(str, ori, fixed, -1)
		s.log.Debug("CSS Element relinked", zap.String("URL", ori), zap.String("Fixed", fixed))
	}

	return bytes.NewBufferString(str)
}
<commit_msg>Document cssPath pointer being to new URL object<commit_after>package scraper

import (
	"bytes"
	"fmt"
	"net/url"
	"path"
	"regexp"
	"strings"

	"github.com/gorilla/css/scanner"
	"github.com/headzoo/surf/browser"
	"go.uber.org/zap"
)

var cssURLRe = regexp.MustCompile(`^url\(['"]?(.*?)['"]?\)$`)

func (s *Scraper) checkCSSForURLs(URL *url.URL, buf *bytes.Buffer) *bytes.Buffer {
	replacings := make(map[string]string)
	str := buf.String()
	css := scanner.New(str)

	for {
		token := css.Next()
		if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
			break
		}

		if token.Type != scanner.TokenURI {
			continue
		}

		match := cssURLRe.FindStringSubmatch(token.Value)
		if match == nil {
			continue
		}

		src := match[1]
		if strings.HasPrefix(strings.ToLower(src), "data:") {
			continue // skip embedded data
		}

		u, err := url.Parse(src)
		if err != nil {
			return buf
		}
		u = URL.ResolveReference(u)

		// Create new URL object and a pointer to it.
		cssPath := *URL
		cssPath.Path = path.Dir(cssPath.Path) + "/"

		img := browser.NewImageAsset(u, "", "", "")
		s.imagesQueue = append(s.imagesQueue, &img.DownloadableAsset)

		resolved := s.resolveURL(&cssPath, src, false, "")
		replacings[token.Value] = resolved
	}

	if len(replacings) == 0 {
		return buf
	}

	for ori, fpath := range replacings {
		fixed := fmt.Sprintf("url(%s)", fpath)
		str = strings.Replace(str, ori, fixed, -1)
		s.log.Debug("CSS Element relinked", zap.String("URL", ori), zap.String("Fixed", fixed))
	}

	return bytes.NewBufferString(str)
}
<|endoftext|>


<commit_before>package v1alpha1

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

	log "github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/retry"

	"github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
	"github.com/caicloud/cyclone/pkg/meta"
	s_v1alpha1 "github.com/caicloud/cyclone/pkg/server/apis/v1alpha1"
	"github.com/caicloud/cyclone/pkg/server/biz/scm"
	"github.com/caicloud/cyclone/pkg/server/common"
	"github.com/caicloud/cyclone/pkg/server/config"
	"github.com/caicloud/cyclone/pkg/server/handler"
	utilhttp "github.com/caicloud/cyclone/pkg/util/http"
	"github.com/caicloud/cyclone/pkg/workflow/workload/pod"
)

// HandleWorkflowRunNotification handles workflowrun finished notification from workflow engine.
func HandleWorkflowRunNotification(ctx context.Context, wfr *v1alpha1.WorkflowRun) (interface{}, error) {
	err := updatePullRequestStatus(wfr)
	if err != nil {
		log.WithField("wfr", wfr.Name).Error("Failed to update SCM status: ", err)
	}

	err = sendNotifications(wfr)
	if err != nil {
		log.WithField("wfr", wfr.Name).Error("Failed to send notifications: ", err)
	}

	// Always return success as have received the workflowrun notifications.
	// No need to return the errors when handle it.
	return nil, nil
}

// sendNotifications send notifications to subscribe systems, and record the results in status.
func sendNotifications(wfr *v1alpha1.WorkflowRun) error {
	// If already there are notification status, no need to send notifications again.
	if wfr.Status.Notifications != nil {
		return nil
	}

	// Send notifications with workflowrun.
	bodyBytes, err := json.Marshal(wfr)
	if err != nil {
		log.WithField("wfr", wfr.Name).Error("Failed to marshal workflowrun: ", err)
		return err
	}
	body := bytes.NewReader(bodyBytes)

	status := make(map[string]v1alpha1.NotificationStatus)
	for _, endpoint := range config.Config.Notifications {
		req, err := http.NewRequest(http.MethodPost, endpoint.URL, body)
		if err != nil {
			err = fmt.Errorf("Failed to new notification request: %v", err)
			log.WithField("wfr", wfr.Name).Error(err)
			status[endpoint.Name] = v1alpha1.NotificationStatus{
				Result:  v1alpha1.NotificationResultFailed,
				Message: err.Error(),
			}
			continue
		}
		// Set Json content type in Http header.
		req.Header.Set(utilhttp.HeaderContentType, utilhttp.HeaderContentTypeJSON)

		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			s := v1alpha1.NotificationStatus{
				Result: v1alpha1.NotificationResultFailed,
			}

			log.WithField("wfr", wfr.Name).Errorf("Failed to send notification for %s: %v", endpoint.Name, err)
			if resp != nil {
				defer resp.Body.Close()
				body, err := ioutil.ReadAll(resp.Body)
				if err != nil {
					log.Error(err)
					s.Message = err.Error()
				} else {
					s.Message = fmt.Sprintf("Status code: %d, error: %s", resp.StatusCode, body)
				}
			}

			status[endpoint.Name] = s
			continue
		}

		log.WithField("wfr", wfr.Name).Infof("Status code of notification for %s: %d", endpoint.Name, resp.StatusCode)
		status[endpoint.Name] = v1alpha1.NotificationStatus{
			Result:  v1alpha1.NotificationResultSucceeded,
			Message: fmt.Sprintf("Status code: %d", resp.StatusCode),
		}
	}

	// Update WorkflowRun notification status with retry.
	err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Get latest WorkflowRun.
		latest, err := handler.K8sClient.CycloneV1alpha1().WorkflowRuns(wfr.Namespace).Get(wfr.Name, metav1.GetOptions{})
		if err != nil {
			return err
		}

		if latest.Status.Notifications == nil {
			latest.Status.Notifications = status
			_, err = handler.K8sClient.CycloneV1alpha1().WorkflowRuns(wfr.Namespace).Update(latest)
			return err
		}

		return nil
	})

	if err != nil {
		log.WithField("name", wfr.Name).Error("Update workflowrun notification status error: ", err)
	}

	return err
}

func updatePullRequestStatus(wfr *v1alpha1.WorkflowRun) error {
	wfrName := wfr.Name
	if wfr.Annotations == nil {
		return fmt.Errorf("Annotations of workflowrun %s can not be empty", wfrName)
	}

	trigger, ok := wfr.Annotations[meta.AnnotationWorkflowRunTrigger]
	if !ok {
		return fmt.Errorf("Trigger of workflowrun %s can not be empty", wfrName)
	}

	// Skip workflowruns whose trigger are not about pull request.
	if trigger != string(scm.PullRequestCommentEventType) && trigger != string(scm.PullRequestEventType) {
		return nil
	}

	labels := wfr.Labels
	if labels == nil {
		return fmt.Errorf("Labels of workflowrun %s can not be empty", wfrName)
	}

	var tenant, project, wfName string
	tenant = common.NamespaceTenant(wfr.Namespace)
	if project, ok = labels[meta.LabelProjectName]; !ok {
		return fmt.Errorf("Fail to get project name from labels of workflowrun %s", wfrName)
	}
	if wfName, ok = labels[meta.LabelWorkflowName]; !ok {
		return fmt.Errorf("Fail to get workflow name from labels of workflowrun %s", wfrName)
	}

	scm, err := getSCMSourceFromWorkflowRun(wfr)
	if err != nil {
		return err
	}

	event, err := getSCMEventData(wfr.Annotations)
	if err != nil {
		return err
	}

	recordURL, err := generateRecordURL(tenant, project, wfName, wfrName)
	if err != nil {
		return err
	}

	return createSCMStatus(scm, wfr.Status.Overall.Phase, recordURL, event)
}

func getSCMSourceFromWorkflowRun(wfr *v1alpha1.WorkflowRun) (*s_v1alpha1.SCMSource, error) {
	// Parse "SCM_AUTH" from resource parameters. ONLY support one SCM git resource.
	var gitToken string
	found := false
	for _, r := range wfr.Spec.Resources {
		for i, p := range r.Parameters {
			if p.Name == "SCM_AUTH" {
				if r.Parameters[i].Value != nil {
					gitToken = *r.Parameters[i].Value
				}
				found = true
				break
			}
		}

		if found {
			break
		}
	}

	if found {
		secretRef := pod.NewSecretRefValue()
		if err := secretRef.Parse(gitToken); err != nil {
			return nil, err
		}

		in, err := getIntegration(secretRef.Namespace, secretRef.Secret)
		if err != nil {
			return nil, err
		}
		if in.Spec.Type != s_v1alpha1.SCM {
			return nil, fmt.Errorf("Type of integration %s is %s, should be %s", in.Name, in.Spec.Type, s_v1alpha1.SCM)
		}

		return in.Spec.SCM, nil
	}

	return nil, fmt.Errorf("Can not get SCM source from workflowrun %s", wfr.Name)
}
<commit_msg>fix: skip notifications for workflowruns without trigger (#1069)<commit_after>package v1alpha1

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

	log "github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/retry"

	"github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
	"github.com/caicloud/cyclone/pkg/meta"
	s_v1alpha1 "github.com/caicloud/cyclone/pkg/server/apis/v1alpha1"
	"github.com/caicloud/cyclone/pkg/server/biz/scm"
	"github.com/caicloud/cyclone/pkg/server/common"
	"github.com/caicloud/cyclone/pkg/server/config"
	"github.com/caicloud/cyclone/pkg/server/handler"
	utilhttp "github.com/caicloud/cyclone/pkg/util/http"
	"github.com/caicloud/cyclone/pkg/workflow/workload/pod"
)

// HandleWorkflowRunNotification handles workflowrun finished notification from workflow engine.
func HandleWorkflowRunNotification(ctx context.Context, wfr *v1alpha1.WorkflowRun) (interface{}, error) {
	err := updatePullRequestStatus(wfr)
	if err != nil {
		log.WithField("wfr", wfr.Name).Error("Failed to update SCM status: ", err)
	}

	err = sendNotifications(wfr)
	if err != nil {
		log.WithField("wfr", wfr.Name).Error("Failed to send notifications: ", err)
	}

	// Always return success as have received the workflowrun notifications.
	// No need to return the errors when handle it.
	return nil, nil
}

// sendNotifications send notifications to subscribe systems, and record the results in status.
func sendNotifications(wfr *v1alpha1.WorkflowRun) error {
	// If already there are notification status, no need to send notifications again.
	if wfr.Status.Notifications != nil {
		return nil
	}

	// Send notifications with workflowrun.
	bodyBytes, err := json.Marshal(wfr)
	if err != nil {
		log.WithField("wfr", wfr.Name).Error("Failed to marshal workflowrun: ", err)
		return err
	}
	body := bytes.NewReader(bodyBytes)

	status := make(map[string]v1alpha1.NotificationStatus)
	for _, endpoint := range config.Config.Notifications {
		req, err := http.NewRequest(http.MethodPost, endpoint.URL, body)
		if err != nil {
			err = fmt.Errorf("Failed to new notification request: %v", err)
			log.WithField("wfr", wfr.Name).Error(err)
			status[endpoint.Name] = v1alpha1.NotificationStatus{
				Result:  v1alpha1.NotificationResultFailed,
				Message: err.Error(),
			}
			continue
		}
		// Set Json content type in Http header.
		req.Header.Set(utilhttp.HeaderContentType, utilhttp.HeaderContentTypeJSON)

		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			s := v1alpha1.NotificationStatus{
				Result: v1alpha1.NotificationResultFailed,
			}

			log.WithField("wfr", wfr.Name).Errorf("Failed to send notification for %s: %v", endpoint.Name, err)
			if resp != nil {
				defer resp.Body.Close()
				body, err := ioutil.ReadAll(resp.Body)
				if err != nil {
					log.Error(err)
					s.Message = err.Error()
				} else {
					s.Message = fmt.Sprintf("Status code: %d, error: %s", resp.StatusCode, body)
				}
			}

			status[endpoint.Name] = s
			continue
		}

		log.WithField("wfr", wfr.Name).Infof("Status code of notification for %s: %d", endpoint.Name, resp.StatusCode)
		status[endpoint.Name] = v1alpha1.NotificationStatus{
			Result:  v1alpha1.NotificationResultSucceeded,
			Message: fmt.Sprintf("Status code: %d", resp.StatusCode),
		}
	}

	// Update WorkflowRun notification status with retry.
	err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Get latest WorkflowRun.
		latest, err := handler.K8sClient.CycloneV1alpha1().WorkflowRuns(wfr.Namespace).Get(wfr.Name, metav1.GetOptions{})
		if err != nil {
			return err
		}

		if latest.Status.Notifications == nil {
			latest.Status.Notifications = status
			_, err = handler.K8sClient.CycloneV1alpha1().WorkflowRuns(wfr.Namespace).Update(latest)
			return err
		}

		return nil
	})

	if err != nil {
		log.WithField("name", wfr.Name).Error("Update workflowrun notification status error: ", err)
	}

	return err
}

func updatePullRequestStatus(wfr *v1alpha1.WorkflowRun) error {
	wfrName := wfr.Name
	if wfr.Annotations == nil {
		return fmt.Errorf("Annotations of workflowrun %s can not be empty", wfrName)
	}

	trigger, ok := wfr.Annotations[meta.AnnotationWorkflowRunTrigger]
	if !ok {
		// Skip workflowruns without trigger.
		return nil
	}

	// Skip workflowruns whose trigger are not about pull request.
	if trigger != string(scm.PullRequestCommentEventType) && trigger != string(scm.PullRequestEventType) {
		return nil
	}

	labels := wfr.Labels
	if labels == nil {
		return fmt.Errorf("Labels of workflowrun %s can not be empty", wfrName)
	}

	var tenant, project, wfName string
	tenant = common.NamespaceTenant(wfr.Namespace)
	if project, ok = labels[meta.LabelProjectName]; !ok {
		return fmt.Errorf("Fail to get project name from labels of workflowrun %s", wfrName)
	}
	if wfName, ok = labels[meta.LabelWorkflowName]; !ok {
		return fmt.Errorf("Fail to get workflow name from labels of workflowrun %s", wfrName)
	}

	scm, err := getSCMSourceFromWorkflowRun(wfr)
	if err != nil {
		return err
	}

	event, err := getSCMEventData(wfr.Annotations)
	if err != nil {
		return err
	}

	recordURL, err := generateRecordURL(tenant, project, wfName, wfrName)
	if err != nil {
		return err
	}

	return createSCMStatus(scm, wfr.Status.Overall.Phase, recordURL, event)
}

func getSCMSourceFromWorkflowRun(wfr *v1alpha1.WorkflowRun) (*s_v1alpha1.SCMSource, error) {
	// Parse "SCM_AUTH" from resource parameters. ONLY support one SCM git resource.
	var gitToken string
	found := false
	for _, r := range wfr.Spec.Resources {
		for i, p := range r.Parameters {
			if p.Name == "SCM_AUTH" {
				if r.Parameters[i].Value != nil {
					gitToken = *r.Parameters[i].Value
				}
				found = true
				break
			}
		}

		if found {
			break
		}
	}

	if found {
		secretRef := pod.NewSecretRefValue()
		if err := secretRef.Parse(gitToken); err != nil {
			return nil, err
		}

		in, err := getIntegration(secretRef.Namespace, secretRef.Secret)
		if err != nil {
			return nil, err
		}
		if in.Spec.Type != s_v1alpha1.SCM {
			return nil, fmt.Errorf("Type of integration %s is %s, should be %s", in.Name, in.Spec.Type, s_v1alpha1.SCM)
		}

		return in.Spec.SCM, nil
	}

	return nil, fmt.Errorf("Can not get SCM source from workflowrun %s", wfr.Name)
}
<|endoftext|>


<commit_before>package cache

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/go-baa/cache/lru"
)

const (
	// MemoryLimit default memory size limit, 128mb
	// 128  1 << 7
	// 1024 1 << 10
	MemoryLimit int64 = 1 << 27
	// MemoryLimitMin minimum value for memory limit, 1mb
	MemoryLimitMin int64 = 1 << 20
	// MenoryObjectMaxSize maximum bytes for object, 1mb
	MenoryObjectMaxSize int64 = 1 << 20
)

// Memory implement a memory cache adapter for cacher
type Memory struct {
	Name       string
	Prefix     string
	bytes      int64
	bytesLimit int64
	mu         sync.RWMutex
	store      *lru.Cache
}

// NewMemory create a cache instance of memory
func NewMemory() Cacher {
	return new(Memory)
}

// Exist return true if value cached by given key
func (c *Memory) Exist(key string) bool {
	item := c.get(c.Prefix + key)
	if item != nil {
		return true
	}
	return false
}

// Get returns value by given key
func (c *Memory) Get(key string, out interface{}) error {
	c.mu.RLock()
	c.mu.RUnlock()
	item := c.get(c.Prefix + key)
	if item == nil {
		return errors.New("cache: cache miss")
	}
	return item.Decode(out)
}

func (c *Memory) get(key string) *Item {
	v, ok := c.store.Get(key)
	if !ok {
		return nil
	}
	item, err := v.(ItemBinary).Item()
	if err != nil {
		return nil
	}
	if item.Expired() {
		c.Delete(key)
		return nil
	}
	return item
}

// Set cache value by given key, cache ttl second
func (c *Memory) Set(key string, v interface{}, ttl int64) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	item := NewItem(v, ttl)
	b, err := item.Encode()
	if err != nil {
		return err
	}
	// if overwrite bytes count will error
	// so, delete first if exist
	if c.Exist(key) {
		c.store.Remove(c.Prefix + key)
	}
	l := int64(len(b))
	err = c.gc(l)
	if err != nil {
		return err
	}
	c.store.Add(c.Prefix+key, b)
	c.bytes += l

	return nil
}

// Incr increases cached int-type value by given key as a counter
// if key not exist, before increase set value with zero
func (c *Memory) Incr(key string) (int64, error) {
	item := c.get(c.Prefix + key)
	if item == nil {
		item = NewItem(0, 0)
	}
	err := item.Incr()
	if err != nil {
		return 0, err
	}
	ttl := item.TTL
	if ttl > 0 {
		ttl = int64((item.Expiration - time.Now().UnixNano()) / 1e9)
	}
	if ttl < 0 {
		return 0, fmt.Errorf("cache: expired")
	}
	err = c.Set(key, item.Val, ttl)
	if err != nil {
		return 0, err
	}
	return item.Val.(int64), nil
}

// Decr decreases cached int-type value by given key as a counter
// if key not exist, return errors
func (c *Memory) Decr(key string) (int64, error) {
	item := c.get(c.Prefix + key)
	if item == nil {
		item = NewItem(0, 0)
	}
	err := item.Decr()
	if err != nil {
		return 0, err
	}
	ttl := item.TTL
	if ttl > 0 {
		ttl = int64((item.Expiration - time.Now().UnixNano()) / 1e9)
	}
	if ttl < 0 {
		return 0, fmt.Errorf("cache: expired")
	}
	err = c.Set(key, item.Val, ttl)
	if err != nil {
		return 0, err
	}
	return item.Val.(int64), nil
}

// Delete delete cached data by given key
func (c *Memory) Delete(key string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.store.Remove(c.Prefix + key)
	return nil
}

// Flush flush cacher
func (c *Memory) Flush() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	var l int
	for {
		l = c.store.Len()
		if l > 0 {
			c.store.RemoveOldest()
		} else {
			break
		}
	}
	c.bytes = 0

	return nil
}

// Start new a cacher and start service
func (c *Memory) Start(o Options) error {
	c.Name = o.Name
	c.Prefix = o.Prefix
	c.bytesLimit = MemoryLimit
	if o.Config != nil {
		if v, ok := o.Config["bytesLimit"].(int64); ok {
			c.bytesLimit = v
		}
	}
	if c.bytesLimit < MemoryLimitMin {
		c.bytesLimit = MemoryLimitMin
	}

	if c.store == nil {
		c.store = lru.New(0)
		c.store.OnEvicted = func(key lru.Key, value interface{}) {
			c.bytes -= int64(len(value.(ItemBinary)))
		}
	}

	return nil
}

// gc release memory for storage new item
// if free bytes can store item returns
// remove items until bytes less than bytesLimit - size
func (c *Memory) gc(size int64) error {
	if c.bytes+size < c.bytesLimit {
		return nil
	}

	if size > MenoryObjectMaxSize {
		return fmt.Errorf("cache: object size limit to %d bytes", MenoryObjectMaxSize)
	}

	releaseSize := c.bytesLimit - size*2
	if releaseSize <= 0 {
		releaseSize = c.bytesLimit - size
	}
	for c.bytes > releaseSize {
		if c.store.Len() > 0 {
			c.store.RemoveOldest()
		} else {
			break
		}
	}
	return nil
}

func init() {
	Register("memory", NewMemory)
}
<commit_msg>fix dead lock when set an expired key<commit_after>package cache

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/go-baa/cache/lru"
)

const (
	// MemoryLimit default memory size limit, 128mb
	// 128  1 << 7
	// 1024 1 << 10
	MemoryLimit int64 = 1 << 27
	// MemoryLimitMin minimum value for memory limit, 1mb
	MemoryLimitMin int64 = 1 << 20
	// MenoryObjectMaxSize maximum bytes for object, 1mb
	MenoryObjectMaxSize int64 = 1 << 20
)

// Memory implement a memory cache adapter for cacher
type Memory struct {
	Name       string
	Prefix     string
	bytes      int64
	bytesLimit int64
	mu         sync.RWMutex
	store      *lru.Cache
}

// NewMemory create a cache instance of memory
func NewMemory() Cacher {
	return new(Memory)
}

// Exist return true if value cached by given key
func (c *Memory) Exist(key string) bool {
	item := c.get(c.Prefix + key)
	if item != nil {
		return true
	}
	return false
}

// Get returns value by given key
func (c *Memory) Get(key string, out interface{}) error {
	c.mu.RLock()
	c.mu.RUnlock()
	item := c.get(c.Prefix + key)
	if item == nil {
		return errors.New("cache: cache miss")
	}
	return item.Decode(out)
}

func (c *Memory) get(key string) *Item {
	v, ok := c.store.Get(key)
	if !ok {
		return nil
	}
	item, err := v.(ItemBinary).Item()
	if err != nil {
		return nil
	}
	if item.Expired() {
		c.Delete(key)
		return nil
	}
	return item
}

// Set cache value by given key, cache ttl second
func (c *Memory) Set(key string, v interface{}, ttl int64) error {
	item := NewItem(v, ttl)
	b, err := item.Encode()
	if err != nil {
		return err
	}

	// if overwrite bytes count will error
	// so, delete first if exist
	if c.Exist(key) {
		c.store.Remove(c.Prefix + key)
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	l := int64(len(b))
	err = c.gc(l)
	if err != nil {
		return err
	}
	c.store.Add(c.Prefix+key, b)
	c.bytes += l

	return nil
}

// Incr increases cached int-type value by given key as a counter
// if key not exist, before increase set value with zero
func (c *Memory) Incr(key string) (int64, error) {
	item := c.get(c.Prefix + key)
	if item == nil {
		item = NewItem(0, 0)
	}
	err := item.Incr()
	if err != nil {
		return 0, err
	}
	ttl := item.TTL
	if ttl > 0 {
		ttl = int64((item.Expiration - time.Now().UnixNano()) / 1e9)
	}
	if ttl < 0 {
		return 0, fmt.Errorf("cache: expired")
	}
	err = c.Set(key, item.Val, ttl)
	if err != nil {
		return 0, err
	}
	return item.Val.(int64), nil
}

// Decr decreases cached int-type value by given key as a counter
// if key not exist, return errors
func (c *Memory) Decr(key string) (int64, error) {
	item := c.get(c.Prefix + key)
	if item == nil {
		item = NewItem(0, 0)
	}
	err := item.Decr()
	if err != nil {
		return 0, err
	}
	ttl := item.TTL
	if ttl > 0 {
		ttl = int64((item.Expiration - time.Now().UnixNano()) / 1e9)
	}
	if ttl < 0 {
		return 0, fmt.Errorf("cache: expired")
	}
	err = c.Set(key, item.Val, ttl)
	if err != nil {
		return 0, err
	}
	return item.Val.(int64), nil
}

// Delete delete cached data by given key
func (c *Memory) Delete(key string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.store.Remove(c.Prefix + key)
	return nil
}

// Flush flush cacher
func (c *Memory) Flush() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	var l int
	for {
		l = c.store.Len()
		if l > 0 {
			c.store.RemoveOldest()
		} else {
			break
		}
	}
	c.bytes = 0

	return nil
}

// Start new a cacher and start service
func (c *Memory) Start(o Options) error {
	c.Name = o.Name
	c.Prefix = o.Prefix
	c.bytesLimit = MemoryLimit
	if o.Config != nil {
		if v, ok := o.Config["bytesLimit"].(int64); ok {
			c.bytesLimit = v
		}
	}
	if c.bytesLimit < MemoryLimitMin {
		c.bytesLimit = MemoryLimitMin
	}

	if c.store == nil {
		c.store = lru.New(0)
		c.store.OnEvicted = func(key lru.Key, value interface{}) {
			c.bytes -= int64(len(value.(ItemBinary)))
		}
	}

	return nil
}

// gc release memory for storage new item
// if free bytes can store item returns
// remove items until bytes less than bytesLimit - size
func (c *Memory) gc(size int64) error {
	if c.bytes+size < c.bytesLimit {
		return nil
	}

	if size > MenoryObjectMaxSize {
		return fmt.Errorf("cache: object size limit to %d bytes", MenoryObjectMaxSize)
	}

	releaseSize := c.bytesLimit - size*2
	if releaseSize <= 0 {
		releaseSize = c.bytesLimit - size
	}
	for c.bytes > releaseSize {
		if c.store.Len() > 0 {
			c.store.RemoveOldest()
		} else {
			break
		}
	}
	return nil
}

func init() {
	Register("memory", NewMemory)
}
<|endoftext|>


<commit_before>package watch

import (
	"fmt"

	"k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	kubev1 "kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/log"
)

type VMInitializer struct {
	clientset  kubecli.KubevirtClient
	restClient *rest.RESTClient
	queue      workqueue.RateLimitingInterface
	store      cache.Store
}

const initializerMarking = "virtualmachines.kubevirt.io"

// FIXME: Both the restClient and clientSet are probably not needed.
func NewVMInitializer(restClient *rest.RESTClient, queue workqueue.RateLimitingInterface, vmCache cache.Store, clientset kubecli.KubevirtClient) *VMInitializer {
	vmi := VMInitializer{
		restClient: restClient,
		clientset:  clientset,
		store:      vmCache,
		queue:      queue,
	}
	return &vmi
}

func (c *VMInitializer) initializeVM(vm *kubev1.VirtualMachine) error {
	// All VM's must be marked as initialized or they are held in limbo forever
	// Collect all errors and defer returning until after the update
	logger := log.Log
	errors := []error{}

	logger.Object(vm).Info("Initializing VM")

	list, err := listPresets(c.restClient, vm.GetNamespace(), labels.Everything())
	//list, err := c.clientset.VirtualMachinePresets(a.GetNamespace()).List(labels.Everything())

	presets, err := filterPresets(list, vm)

	if err != nil {
		logger.Object(vm).Reason(err).Errorf("Error while matching presets to VirtualMachine")
		errors = append(errors, err)
	}

	if len(presets) != 0 {
		err = checkPresetMergeConflicts(vm, presets)

		if err != nil {
			logger.Reason(err).Errorf("Conflicting VM Presets")
			errors = append(errors, fmt.Errorf("conflicting vm presets"))
		}

		err = applyPresets(vm, presets)
		if err != nil {
			// A more specific error should have been logged during the applyPresets call.
			// We don't know *which* preset in the list was problematic at this level.
			logger.Object(vm).Errorf("Unable to apply presets to virtualmachine: %v", err)
			errors = append(errors, err)
		}
	}

	logger.Object(vm).Info("Marking VM as initialized and updating")
	removeInitializer(vm)
	c.clientset.VM(vm.Namespace).Update(vm)

	if len(errors) > 0 {
		utilerrors.NewAggregate(errors)
	}
	return nil
}

// FIXME: this should be integrated into the KubeVirt clientset
func listPresets(restClient *rest.RESTClient, namespace string, selector labels.Selector) ([]kubev1.VirtualMachinePreset, error) {
	options := k8smetav1.ListOptions{LabelSelector: selector.String()}
	presetList := &kubev1.VirtualMachinePresetList{}
	if err := restClient.Get().
		Resource("virtualmachinepresets").
		Namespace(namespace).
		VersionedParams(&options, scheme.ParameterCodec).
		Do().
		Into(presetList); err != nil {
		return nil, err
	}
	for _, preset := range presetList.Items {
		preset.SetGroupVersionKind(kubev1.VirtualMachinePresetGroupVersionKind)
	}

	return presetList.Items, nil
}

// filterPresets returns list of VirtualMachinePresets which match given VirtualMachine.
func filterPresets(list []kubev1.VirtualMachinePreset, vm *kubev1.VirtualMachine) ([]kubev1.VirtualMachinePreset, error) {
	var matchingPresets []kubev1.VirtualMachinePreset

	logger := log.Log

	for _, preset := range list {
		selector, err := k8smetav1.LabelSelectorAsSelector(&preset.Spec.Selector)
		if err != nil {
			logger.Reason(err).Errorf("label selector conversion failed: %v for selector: %v", preset.Spec.Selector, err)
			return nil, fmt.Errorf("label selector conversion failed: %v for selector: %v", preset.Spec.Selector, err)
		}

		// check if the pod labels match the selector
		if !selector.Matches(labels.Set(vm.Labels)) {
			continue
		}
		logger.Infof("VirtualMachinePreset %s matches labels of VM %s", preset.GetName(), vm.GetName())
		matchingPresets = append(matchingPresets, preset)
	}
	return matchingPresets, nil
}

func checkPresetMergeConflicts(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset) error {
	//FIXME: implement
	return nil
}

func mergeDomainSpec(presetSpec *kubev1.DomainSpec, vmSpec *kubev1.DomainSpec) error {
	if len(presetSpec.Resources.Requests) > 0 {
		if vmSpec.Resources.Requests == nil {
			vmSpec.Resources.Requests = v1.ResourceList{}
		}
		for key, val := range presetSpec.Resources.Requests {
			if vmSpec.Resources.Requests[key] != val {
			}
			vmSpec.Resources.Requests[key] = val
		}
	}
	if presetSpec.CPU != nil {
		presetSpec.CPU.DeepCopyInto(vmSpec.CPU)
	}
	if presetSpec.Firmware != nil {
		presetSpec.Firmware.DeepCopyInto(vmSpec.Firmware)
	}
	if presetSpec.Clock != nil {
		if vmSpec.Clock == nil {
			vmSpec.Clock = new(kubev1.Clock)
		}
		vmSpec.Clock.ClockOffset = presetSpec.Clock.ClockOffset
		if presetSpec.Clock.Timer != nil {
			presetSpec.Clock.Timer.DeepCopyInto(vmSpec.Clock.Timer)
		}
	}
	if presetSpec.Features != nil {
		presetSpec.Features.DeepCopyInto(vmSpec.Features)
	}
	if presetSpec.Devices.Watchdog != nil {
		presetSpec.Devices.Watchdog.DeepCopyInto(vmSpec.Devices.Watchdog)

		// FIXME: this is slightly wrong. There can exist exact duplicates
		// in the destination list which must be weeded out.
		// We do not need to worry about conflicting devices here
		// checkPresetMergeConflicts would have already raised an error
		vmSpec.Devices.Disks = append(vmSpec.Devices.Disks, presetSpec.Devices.Disks...)
	}
	return nil
}

func applyPresets(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset) error {
	for _, preset := range presets {
		err := mergeDomainSpec(preset.Spec.Domain, &vm.Spec.Domain)
		if err != nil {
			return err
		}
	}

	err := annotateVM(vm, presets)
	if err != nil {
		return err
	}
	return nil
}

func removeInitializer(vm *kubev1.VirtualMachine) {
	newInitilizers := []k8smetav1.Initializer{}
	for _, i := range vm.Initializers.Pending {
		if i.Name != initializerMarking {
			newInitilizers = append(newInitilizers, i)
		}
	}
	vm.Initializers.Pending = newInitilizers
}

func annotateVM(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset) error {
	//FIXME: implement
	return nil
}
<commit_msg>Annotate VM's upon successful completion<commit_after>package watch

import (
	"fmt"
	"strings"

	"k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	kubev1 "kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/log"
)

type VMInitializer struct {
	clientset  kubecli.KubevirtClient
	restClient *rest.RESTClient
	queue      workqueue.RateLimitingInterface
	store      cache.Store
}

const initializerMarking = "virtualmachines.kubevirt.io"
const annotationPrefix = "virtualmachinepreset.admission.kubernetes.io/virtualmachinepreset"

// FIXME: Both the restClient and clientSet are probably not needed.
func NewVMInitializer(restClient *rest.RESTClient, queue workqueue.RateLimitingInterface, vmCache cache.Store, clientset kubecli.KubevirtClient) *VMInitializer {
	vmi := VMInitializer{
		restClient: restClient,
		clientset:  clientset,
		store:      vmCache,
		queue:      queue,
	}
	return &vmi
}

func (c *VMInitializer) initializeVM(vm *kubev1.VirtualMachine) error {
	// All VM's must be marked as initialized or they are held in limbo forever
	// Collect all errors and defer returning until after the update
	logger := log.Log
	errors := []error{}

	logger.Object(vm).Info("Initializing VM")

	allPresets, err := listPresets(c.restClient, vm.GetNamespace(), labels.Everything())
	//list, err := c.clientset.VirtualMachinePresets(a.GetNamespace()).List(labels.Everything())

	matchingPresets, err := filterPresets(allPresets, vm)

	if err != nil {
		logger.Object(vm).Reason(err).Errorf("Error while matching presets to VirtualMachine")
		errors = append(errors, err)
	}

	if len(matchingPresets) != 0 {
		err = checkPresetMergeConflicts(vm, matchingPresets)

		if err != nil {
			logger.Reason(err).Errorf("Conflicting VM Presets")
			errors = append(errors, fmt.Errorf("conflicting vm presets"))
		}

		err = applyPresets(vm, matchingPresets)
		if err != nil {
			// A more specific error should have been logged during the applyPresets call.
			// We don't know *which* preset in the list was problematic at this level.
			logger.Object(vm).Errorf("Unable to apply presets to virtualmachine: %v", err)
			errors = append(errors, err)
		}
	}

	logger.Object(vm).Info("Marking VM as initialized and updating")
	removeInitializer(vm)
	c.clientset.VM(vm.Namespace).Update(vm)

	if len(errors) > 0 {
		utilerrors.NewAggregate(errors)
	}
	return nil
}

// FIXME: this should be integrated into the KubeVirt clientset
func listPresets(restClient *rest.RESTClient, namespace string, selector labels.Selector) ([]kubev1.VirtualMachinePreset, error) {
	options := k8smetav1.ListOptions{LabelSelector: selector.String()}
	presetList := &kubev1.VirtualMachinePresetList{}
	if err := restClient.Get().
		Resource("virtualmachinepresets").
		Namespace(namespace).
		VersionedParams(&options, scheme.ParameterCodec).
		Do().
		Into(presetList); err != nil {
		return nil, err
	}
	for _, preset := range presetList.Items {
		preset.SetGroupVersionKind(kubev1.VirtualMachinePresetGroupVersionKind)
	}

	return presetList.Items, nil
}

// filterPresets returns list of VirtualMachinePresets which match given VirtualMachine.
func filterPresets(list []kubev1.VirtualMachinePreset, vm *kubev1.VirtualMachine) ([]kubev1.VirtualMachinePreset, error) {
	var matchingPresets []kubev1.VirtualMachinePreset

	logger := log.Log

	for _, preset := range list {
		selector, err := k8smetav1.LabelSelectorAsSelector(&preset.Spec.Selector)
		if err != nil {
			logger.Reason(err).Errorf("label selector conversion failed: %v for selector: %v", preset.Spec.Selector, err)
			return nil, fmt.Errorf("label selector conversion failed: %v for selector: %v", preset.Spec.Selector, err)
		}

		// check if the pod labels match the selector
		if !selector.Matches(labels.Set(vm.Labels)) {
			continue
		}
		logger.Infof("VirtualMachinePreset %s matches labels of VM %s", preset.GetName(), vm.GetName())
		matchingPresets = append(matchingPresets, preset)
	}
	return matchingPresets, nil
}

func checkPresetMergeConflicts(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset) error {
	//FIXME: implement
	return nil
}

func mergeDomainSpec(presetSpec *kubev1.DomainSpec, vmSpec *kubev1.DomainSpec) error {
	if len(presetSpec.Resources.Requests) > 0 {
		if vmSpec.Resources.Requests == nil {
			vmSpec.Resources.Requests = v1.ResourceList{}
		}
		for key, val := range presetSpec.Resources.Requests {
			if vmSpec.Resources.Requests[key] != val {
			}
			vmSpec.Resources.Requests[key] = val
		}
	}
	if presetSpec.CPU != nil {
		presetSpec.CPU.DeepCopyInto(vmSpec.CPU)
	}
	if presetSpec.Firmware != nil {
		presetSpec.Firmware.DeepCopyInto(vmSpec.Firmware)
	}
	if presetSpec.Clock != nil {
		if vmSpec.Clock == nil {
			vmSpec.Clock = new(kubev1.Clock)
		}
		vmSpec.Clock.ClockOffset = presetSpec.Clock.ClockOffset
		if presetSpec.Clock.Timer != nil {
			presetSpec.Clock.Timer.DeepCopyInto(vmSpec.Clock.Timer)
		}
	}
	if presetSpec.Features != nil {
		presetSpec.Features.DeepCopyInto(vmSpec.Features)
	}
	if presetSpec.Devices.Watchdog != nil {
		presetSpec.Devices.Watchdog.DeepCopyInto(vmSpec.Devices.Watchdog)

		// Devices in the VM should appear first (for mount point ordering)
		// Append all devices from preset, but ignore duplicates.
		deviceSet := make(map[string]bool)
		for _, vmDev := range vmSpec.Devices.Disks {
			deviceSet[vmDev.Name] = true
		}
		for _, presetDev := range presetSpec.Devices.Disks {
			if !deviceSet[presetDev.Name] {
				vmSpec.Devices.Disks = append(vmSpec.Devices.Disks, presetDev)
			}
		}
	}
	return nil
}

func applyPresets(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset) error {
	for _, preset := range presets {
		err := mergeDomainSpec(preset.Spec.Domain, &vm.Spec.Domain)
		if err != nil {
			return err
		}
	}

	err := annotateVM(vm, presets)
	if err != nil {
		return err
	}
	return nil
}

func removeInitializer(vm *kubev1.VirtualMachine) {
	newInitilizers := []k8smetav1.Initializer{}
	for _, i := range vm.Initializers.Pending {
		if i.Name != initializerMarking {
			newInitilizers = append(newInitilizers, i)
		}
	}
	vm.Initializers.Pending = newInitilizers
}

func annotateVM(vm *kubev1.VirtualMachine, presets []kubev1.VirtualMachinePreset) error {
	if vm.Annotations == nil {
		vm.Annotations = map[string]string{}
	}
	for _, preset := range presets {
		kind := strings.ToLower(preset.Kind)
		annotationKey := fmt.Sprintf("%s.%s/%s", kind, kubev1.GroupName, preset.Name)
		vm.Annotations[annotationKey] = preset.APIVersion
	}

	return nil
}
<|endoftext|>


<commit_before>// memory
package main

import (
//	proc "github.com/cespare/goproc"
)

type memData struct {
	memTotal    int64
	memFree     int64
	memPercent  float64
	swapTotal   int64
	swapFree    int64
	swapPercent float64
}
<commit_msg>Corrected types, added only function to be here<commit_after>// memory
package main

import (
	proc "github.com/cespare/goproc"
)

type memData struct {
	memTotal    uint64
	memFree     uint64
	memPercent  float64
	swapTotal   uint64
	swapFree    uint64
	swapPercent float64
}

func (m *memData) Update() error {
	t, err := proc.MemInfo()
	if err != nil {
		return err
	}
	m.memTotal = t["MemTotal"]
	m.memFree = t["MemFree"]
	m.memPercent = float64((m.memTotal - m.memFree) / m.memTotal)

	m.swapTotal = t["SwapTotal"]
	m.swapFree = t["SwapFree"]
	m.swapPercent = float64((m.swapTotal - m.swapFree) / m.swapTotal)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Mender Software AS
//
//    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 main

import (
	"bufio"
	"os"
	"strings"
	"time"

	"github.com/mendersoftware/log"
)

type Controller interface {
	Bootstrap() error
	TransitionState() MenderState
	GetCurrentImageID() string
	LastError() error
}

const (
	defaultManifestFile = "/etc/build_mender"
	defaultKeyFile      = "mender-agent.pem"
	defaultDataStore    = "/var/lib/mender"
)

type MenderState int

// State transitions:
//
//            unknown
//               |
//               v
//             init
//               |
//               v
//          bootstrapped
//               |
//       +-------+-------------+
//       |                     |
//       v                     v
// fresh update         wait for update
//
// Any state can transition to MenderStateError, setting LastError()
// to the error that triggered the transition

const (
	MenderStateUnknown MenderState = iota
	// initial state
	MenderStateInit
	// client is bootstrapped, i.e. ready to go
	MenderStateBootstrapped
	// update applied, waiting for commit
	MenderStateRunningWithFreshUpdate
	// wait for new update
	MenderStateWaitForUpdate
	// error occurred, call Controller.LastError() to obtain the
	// error
	MenderStateError
)

type mender struct {
	BootEnvReadWriter
	state          MenderState
	config         menderFileConfig
	manifestFile   string
	deviceKey      *Keystore
	forceBootstrap bool
	lastError      error
}

func NewMender(env BootEnvReadWriter, store Store) *mender {

	ks := NewKeystore(store)
	if ks == nil {
		return nil
	}

	m := &mender{
		BootEnvReadWriter: env,
		manifestFile:      defaultManifestFile,
		deviceKey:         NewKeystore(store),
		state:             MenderStateInit,
	}

	if err := m.deviceKey.Load(m.config.DeviceKey); err != nil && IsNoKeys(err) == false {
		log.Errorf("failed to load device keys: %s", err)
		return nil
	}

	return m
}

func (m *mender) TransitionState() MenderState {
	m.updateState()
	return m.state
}

func (m mender) GetCurrentImageID() string {
	// This is where Yocto stores buid information
	manifest, err := os.Open(m.manifestFile)
	if err != nil {
		log.Error("Can not read current image id.")
		return ""
	}

	imageID := ""

	scanner := bufio.NewScanner(manifest)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		log.Debug("Read data from device manifest file: ", line)
		if strings.HasPrefix(line, "IMAGE_ID") {
			log.Debug("Found device id line: ", line)
			lineID := strings.Split(line, "=")
			if len(lineID) != 2 {
				log.Errorf("Broken device manifest file: (%v)", lineID)
				return ""
			}
			log.Debug("Current image id: ", strings.TrimSpace(lineID[1]))
			return strings.TrimSpace(lineID[1])
		}
	}
	if err := scanner.Err(); err != nil {
		log.Error(err)
	}
	return imageID
}

func (m *mender) changeState(state MenderState) {
	log.Infof("Mender state: %v -> %v", m.state, state)
	m.state = state
}

func (m *mender) hasUpgrade() (bool, error) {
	env, err := m.ReadEnv("upgrade_available")
	if err != nil {
		return false, err
	}
	upgradeAvailable := env["upgrade_available"]

	// we are after update
	if upgradeAvailable == "1" {
		return true, nil
	}
	return false, nil
}

func (m *mender) updateState() {

	newstate := MenderStateUnknown
	var merr error

	switch m.state {
	case MenderStateInit:
		if m.needsBootstrap() {
			if err := m.doBootstrap(); err != nil {
				newstate = MenderStateError
				merr = err
			} else {
				newstate = MenderStateBootstrapped
			}
		} else {
			newstate = MenderStateBootstrapped
		}
	case MenderStateBootstrapped:
		upg, err := m.hasUpgrade()
		if err != nil {
			newstate = MenderStateError
			merr = err
		} else {
			if upg {
				newstate = MenderStateRunningWithFreshUpdate
			} else {
				newstate = MenderStateWaitForUpdate
			}
		}
	}

	// record last errpr
	if newstate == MenderStateError {
		m.lastError = merr
	}
	m.changeState(newstate)
}

func (m *mender) ForceBootstrap() {
	m.forceBootstrap = true
}

func (m *mender) needsBootstrap() bool {
	if m.forceBootstrap {
		return true
	}

	if m.deviceKey.Private() == nil {
		log.Debugf("needs keys")
		return true
	}

	return false
}

func (m *mender) Bootstrap() error {
	if !m.needsBootstrap() {
		return nil
	}

	return m.doBootstrap()
}

func (m *mender) doBootstrap() error {
	if m.deviceKey.Private() == nil {
		log.Infof("device keys not present, generating")
		if err := m.deviceKey.Generate(); err != nil {
			return err
		}

		if err := m.deviceKey.Save(m.config.DeviceKey); err != nil {
			log.Errorf("faiiled to save keys to %s: %s",
				m.config.DeviceKey, err)
			return err
		}
	}

	m.forceBootstrap = false

	return nil
}

func (m *mender) LastError() error {
	return m.lastError
}
<commit_msg>mender: extend Controler interface with helper for querying poll intervals<commit_after>// Copyright 2016 Mender Software AS
//
//    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 main

import (
	"bufio"
	"os"
	"strings"
	"time"

	"github.com/mendersoftware/log"
)

type Controller interface {
	Bootstrap() error
	TransitionState() MenderState
	GetCurrentImageID() string
	GetUpdatePollInterval() time.Duration
	LastError() error
}

const (
	defaultManifestFile = "/etc/build_mender"
	defaultKeyFile      = "mender-agent.pem"
	defaultDataStore    = "/var/lib/mender"
)

type MenderState int

// State transitions:
//
//            unknown
//               |
//               v
//             init
//               |
//               v
//          bootstrapped
//               |
//       +-------+-------------+
//       |                     |
//       v                     v
// fresh update         wait for update
//
// Any state can transition to MenderStateError, setting LastError()
// to the error that triggered the transition

const (
	MenderStateUnknown MenderState = iota
	// initial state
	MenderStateInit
	// client is bootstrapped, i.e. ready to go
	MenderStateBootstrapped
	// update applied, waiting for commit
	MenderStateRunningWithFreshUpdate
	// wait for new update
	MenderStateWaitForUpdate
	// error occurred, call Controller.LastError() to obtain the
	// error
	MenderStateError
)

type mender struct {
	BootEnvReadWriter
	state          MenderState
	config         menderFileConfig
	manifestFile   string
	deviceKey      *Keystore
	forceBootstrap bool
	lastError      error
}

func NewMender(env BootEnvReadWriter, store Store) *mender {

	ks := NewKeystore(store)
	if ks == nil {
		return nil
	}

	m := &mender{
		BootEnvReadWriter: env,
		manifestFile:      defaultManifestFile,
		deviceKey:         NewKeystore(store),
		state:             MenderStateInit,
	}

	if err := m.deviceKey.Load(m.config.DeviceKey); err != nil && IsNoKeys(err) == false {
		log.Errorf("failed to load device keys: %s", err)
		return nil
	}

	return m
}

func (m *mender) TransitionState() MenderState {
	m.updateState()
	return m.state
}

func (m mender) GetCurrentImageID() string {
	// This is where Yocto stores buid information
	manifest, err := os.Open(m.manifestFile)
	if err != nil {
		log.Error("Can not read current image id.")
		return ""
	}

	imageID := ""

	scanner := bufio.NewScanner(manifest)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		log.Debug("Read data from device manifest file: ", line)
		if strings.HasPrefix(line, "IMAGE_ID") {
			log.Debug("Found device id line: ", line)
			lineID := strings.Split(line, "=")
			if len(lineID) != 2 {
				log.Errorf("Broken device manifest file: (%v)", lineID)
				return ""
			}
			log.Debug("Current image id: ", strings.TrimSpace(lineID[1]))
			return strings.TrimSpace(lineID[1])
		}
	}
	if err := scanner.Err(); err != nil {
		log.Error(err)
	}
	return imageID
}

func (m *mender) changeState(state MenderState) {
	log.Infof("Mender state: %v -> %v", m.state, state)
	m.state = state
}

func (m *mender) hasUpgrade() (bool, error) {
	env, err := m.ReadEnv("upgrade_available")
	if err != nil {
		return false, err
	}
	upgradeAvailable := env["upgrade_available"]

	// we are after update
	if upgradeAvailable == "1" {
		return true, nil
	}
	return false, nil
}

func (m *mender) updateState() {

	newstate := MenderStateUnknown
	var merr error

	switch m.state {
	case MenderStateInit:
		if m.needsBootstrap() {
			if err := m.doBootstrap(); err != nil {
				newstate = MenderStateError
				merr = err
			} else {
				newstate = MenderStateBootstrapped
			}
		} else {
			newstate = MenderStateBootstrapped
		}
	case MenderStateBootstrapped:
		upg, err := m.hasUpgrade()
		if err != nil {
			newstate = MenderStateError
			merr = err
		} else {
			if upg {
				newstate = MenderStateRunningWithFreshUpdate
			} else {
				newstate = MenderStateWaitForUpdate
			}
		}
	}

	// record last errpr
	if newstate == MenderStateError {
		m.lastError = merr
	}
	m.changeState(newstate)
}

func (m *mender) ForceBootstrap() {
	m.forceBootstrap = true
}

func (m *mender) needsBootstrap() bool {
	if m.forceBootstrap {
		return true
	}

	if m.deviceKey.Private() == nil {
		log.Debugf("needs keys")
		return true
	}

	return false
}

func (m *mender) Bootstrap() error {
	if !m.needsBootstrap() {
		return nil
	}

	return m.doBootstrap()
}

func (m *mender) doBootstrap() error {
	if m.deviceKey.Private() == nil {
		log.Infof("device keys not present, generating")
		if err := m.deviceKey.Generate(); err != nil {
			return err
		}

		if err := m.deviceKey.Save(m.config.DeviceKey); err != nil {
			log.Errorf("faiiled to save keys to %s: %s",
				m.config.DeviceKey, err)
			return err
		}
	}

	m.forceBootstrap = false

	return nil
}

func (m *mender) LastError() error {
	return m.lastError
}

func (m mender) GetUpdatePollInterval() time.Duration {
	return time.Duration(m.config.PollIntervalSeconds) * time.Second
}
<|endoftext|>