<commit_before>package proto

import (
	"errors"
	"fmt"

	"github.com/vapourismo/knx-go/knx/util"
)

// TunnelLayer identifies the tunnelling layer for a tunnelling connection.
type TunnelLayer uint8

const (
	// TunnelLayerData establishes a data-link layer tunnel.
	TunnelLayerData TunnelLayer = 0x02

	// TunnelLayerRaw establishes a raw tunnel.
	TunnelLayerRaw TunnelLayer = 0x04

	// TunnelLayerBusmon establishes a bus monitor tunnel.
	TunnelLayerBusmon TunnelLayer = 0x80
)

// A ConnReq requests a connection to a gateway.
type ConnReq struct {
	Control HostInfo
	Tunnel  HostInfo
	Layer   TunnelLayer
}

// Service returns the service identifier for connection requests.
func (ConnReq) Service() ServiceID {
	return ConnReqService
}

var hostInfoSize = HostInfo{}.Size()

// Size returns the packed size.
func (ConnReq) Size() uint {
	return 2*hostInfoSize + 4
}

// Pack the structure into the given buffer.
func (req *ConnReq) Pack(buffer []byte) {
	util.PackSome(buffer, &req.Control, &req.Tunnel)

	buffer = buffer[2*hostInfoSize:]
	buffer[0] = 4
	buffer[1] = 4
	buffer[2] = byte(req.Layer)
	buffer[3] = 0
}

// Unpack initializes the structure by parsing the given data.
func (req *ConnReq) Unpack(data []byte) (n uint, err error) {
	var length, connType, reserved uint8

	n, err = util.UnpackSome(
		data, &req.Control, &req.Tunnel, &length, &connType, (*uint8)(&req.Layer), &reserved,
	)
	if err != nil {
		return
	}

	if length != 4 {
		return n, errors.New("Invalid connection request info structure length")
	}

	if connType != 4 {
		return n, errors.New("Invalid connection type")
	}

	return
}

// ConnResStatus is the type of status code carried in a connection response.
type ConnResStatus uint8

// Therese are known connection response status codes.
const (
	ConnResOk                ConnResStatus = 0x00
	ConnResUnsupportedType   ConnResStatus = 0x22
	ConnResUnsupportedOption ConnResStatus = 0x23
	ConnResBusy              ConnResStatus = 0x24
)

// String describes the status code.
func (status ConnResStatus) String() string {
	switch status {
	case ConnResOk:
		return "Connection established"

	case ConnResUnsupportedType:
		return "Requested connection type is unsupported"

	case ConnResUnsupportedOption:
		return "One of the requested options is unsupported"

	case ConnResBusy:
		return "No data channel is available"

	default:
		return fmt.Sprintf("Unknown status code %#x", uint8(status))
	}
}

// Error implements the error Error method.
func (status ConnResStatus) Error() string {
	return status.String()
}

// ConnRes is a response to a ConnReq.
type ConnRes struct {
	Channel uint8
	Status  ConnResStatus
	Control HostInfo
}

// Service returns the service identifier for connection responses.
func (ConnRes) Service() ServiceID {
	return ConnResService
}

// Unpack initializes the structure by parsing the given data.
func (res *ConnRes) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &res.Channel, (*uint8)(&res.Status), &res.Control)
}

// A ConnStateReq requests the the connection state from a gateway.
type ConnStateReq struct {
	Channel uint8
	Status  uint8
	Control HostInfo
}

// Service returns the service identifier for connection state requests.
func (ConnStateReq) Service() ServiceID {
	return ConnStateReqService
}

// Size returns the packed size.
func (ConnStateReq) Size() uint {
	return 2 + hostInfoSize
}

// Pack the structure into the buffer.
func (req *ConnStateReq) Pack(buffer []byte) {
	buffer[0] = req.Channel
	buffer[1] = req.Status
	req.Control.Pack(buffer[2:])
}

// Unpack initializes the structure by parsing the given data.
func (req *ConnStateReq) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &req.Channel, &req.Status, &req.Control)
}

// A ConnState represents the state of a connection.
type ConnState uint8

// These are known connection states.
const (
	ConnStateNormal    ConnState = 0x00
	ConnStateInactive  ConnState = 0x21
	ConnStateDataError ConnState = 0x26
	ConnStateKNXError  ConnState = 0x27
)

// String converts the connection state to a string.
func (state ConnState) String() string {
	switch state {
	case ConnStateNormal:
		return "Connection is intact"

	case ConnStateInactive:
		return "Connection is inactive"

	case ConnStateDataError:
		return "Gateway encountered a data error"

	case ConnStateKNXError:
		return "Gateway encountered a KNX error"

	default:
		return fmt.Sprintf("Unknown connection state %#x", uint8(state))
	}
}

// A ConnStateRes is a response to a ConnStateReq.
type ConnStateRes struct {
	Channel uint8
	Status  ConnState
}

// Service returns the service identifier for connection state responses.
func (ConnStateRes) Service() ServiceID {
	return ConnStateResService
}

// Size returns the packed size.
func (ConnStateRes) Size() uint {
	return 2
}

// Pack the structure into the buffer.
func (res *ConnStateRes) Pack(buffer []byte) {
	buffer[0] = res.Channel
	buffer[1] = byte(res.Status)
}

// Unpack initializes the structure by parsing the given data.
func (res *ConnStateRes) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &res.Channel, &res.Status)
}

// A DiscReq requests a connection to be terminated.
type DiscReq struct {
	Channel uint8
	Status  uint8
	Control HostInfo
}

// Service returns the service identifier for disconnect requests.
func (DiscReq) Service() ServiceID {
	return DiscReqService
}

// Size returns the packed size.
func (DiscReq) Size() uint {
	return 2 + hostInfoSize
}

// Pack the structure into the buffer.
func (req *DiscReq) Pack(buffer []byte) {
	buffer[0] = req.Channel
	buffer[1] = req.Status
	req.Control.Pack(buffer[2:])
}

// Unpack initializes the structure by parsing the given data.
func (req *DiscReq) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &req.Channel, &req.Status, &req.Control)
}

// A DiscRes is a response to a DiscReq..
type DiscRes struct {
	Channel uint8
	Status  uint8
}

// Service returns the service identifier for disconnect responses.
func (DiscRes) Service() ServiceID {
	return DiscResService
}

// Size returns the packed size.
func (DiscRes) Size() uint {
	return 2
}

// Pack the structure into the buffer.
func (res *DiscRes) Pack(data []byte) {
	data[0] = res.Channel
	data[1] = res.Status
}

// Unpack initializes the structure by parsing the given data.
func (res *DiscRes) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &res.Channel, &res.Status)
}
<commit_msg>Unpack ConnState properly<commit_after>package proto

import (
	"errors"
	"fmt"

	"github.com/vapourismo/knx-go/knx/util"
)

// TunnelLayer identifies the tunnelling layer for a tunnelling connection.
type TunnelLayer uint8

const (
	// TunnelLayerData establishes a data-link layer tunnel.
	TunnelLayerData TunnelLayer = 0x02

	// TunnelLayerRaw establishes a raw tunnel.
	TunnelLayerRaw TunnelLayer = 0x04

	// TunnelLayerBusmon establishes a bus monitor tunnel.
	TunnelLayerBusmon TunnelLayer = 0x80
)

// A ConnReq requests a connection to a gateway.
type ConnReq struct {
	Control HostInfo
	Tunnel  HostInfo
	Layer   TunnelLayer
}

// Service returns the service identifier for connection requests.
func (ConnReq) Service() ServiceID {
	return ConnReqService
}

var hostInfoSize = HostInfo{}.Size()

// Size returns the packed size.
func (ConnReq) Size() uint {
	return 2*hostInfoSize + 4
}

// Pack the structure into the given buffer.
func (req *ConnReq) Pack(buffer []byte) {
	util.PackSome(buffer, &req.Control, &req.Tunnel)

	buffer = buffer[2*hostInfoSize:]
	buffer[0] = 4
	buffer[1] = 4
	buffer[2] = byte(req.Layer)
	buffer[3] = 0
}

// Unpack initializes the structure by parsing the given data.
func (req *ConnReq) Unpack(data []byte) (n uint, err error) {
	var length, connType, reserved uint8

	n, err = util.UnpackSome(
		data, &req.Control, &req.Tunnel, &length, &connType, (*uint8)(&req.Layer), &reserved,
	)
	if err != nil {
		return
	}

	if length != 4 {
		return n, errors.New("Invalid connection request info structure length")
	}

	if connType != 4 {
		return n, errors.New("Invalid connection type")
	}

	return
}

// ConnResStatus is the type of status code carried in a connection response.
type ConnResStatus uint8

// Therese are known connection response status codes.
const (
	ConnResOk                ConnResStatus = 0x00
	ConnResUnsupportedType   ConnResStatus = 0x22
	ConnResUnsupportedOption ConnResStatus = 0x23
	ConnResBusy              ConnResStatus = 0x24
)

// String describes the status code.
func (status ConnResStatus) String() string {
	switch status {
	case ConnResOk:
		return "Connection established"

	case ConnResUnsupportedType:
		return "Requested connection type is unsupported"

	case ConnResUnsupportedOption:
		return "One of the requested options is unsupported"

	case ConnResBusy:
		return "No data channel is available"

	default:
		return fmt.Sprintf("Unknown status code %#x", uint8(status))
	}
}

// Error implements the error Error method.
func (status ConnResStatus) Error() string {
	return status.String()
}

// ConnRes is a response to a ConnReq.
type ConnRes struct {
	Channel uint8
	Status  ConnResStatus
	Control HostInfo
}

// Service returns the service identifier for connection responses.
func (ConnRes) Service() ServiceID {
	return ConnResService
}

// Unpack initializes the structure by parsing the given data.
func (res *ConnRes) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &res.Channel, (*uint8)(&res.Status), &res.Control)
}

// A ConnStateReq requests the the connection state from a gateway.
type ConnStateReq struct {
	Channel uint8
	Status  uint8
	Control HostInfo
}

// Service returns the service identifier for connection state requests.
func (ConnStateReq) Service() ServiceID {
	return ConnStateReqService
}

// Size returns the packed size.
func (ConnStateReq) Size() uint {
	return 2 + hostInfoSize
}

// Pack the structure into the buffer.
func (req *ConnStateReq) Pack(buffer []byte) {
	buffer[0] = req.Channel
	buffer[1] = req.Status
	req.Control.Pack(buffer[2:])
}

// Unpack initializes the structure by parsing the given data.
func (req *ConnStateReq) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &req.Channel, &req.Status, &req.Control)
}

// A ConnState represents the state of a connection.
type ConnState uint8

// These are known connection states.
const (
	ConnStateNormal    ConnState = 0x00
	ConnStateInactive  ConnState = 0x21
	ConnStateDataError ConnState = 0x26
	ConnStateKNXError  ConnState = 0x27
)

// String converts the connection state to a string.
func (state ConnState) String() string {
	switch state {
	case ConnStateNormal:
		return "Connection is intact"

	case ConnStateInactive:
		return "Connection is inactive"

	case ConnStateDataError:
		return "Gateway encountered a data error"

	case ConnStateKNXError:
		return "Gateway encountered a KNX error"

	default:
		return fmt.Sprintf("Unknown connection state %#x", uint8(state))
	}
}

// A ConnStateRes is a response to a ConnStateReq.
type ConnStateRes struct {
	Channel uint8
	Status  ConnState
}

// Service returns the service identifier for connection state responses.
func (ConnStateRes) Service() ServiceID {
	return ConnStateResService
}

// Size returns the packed size.
func (ConnStateRes) Size() uint {
	return 2
}

// Pack the structure into the buffer.
func (res *ConnStateRes) Pack(buffer []byte) {
	buffer[0] = res.Channel
	buffer[1] = byte(res.Status)
}

// Unpack initializes the structure by parsing the given data.
func (res *ConnStateRes) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &res.Channel, (*uint8)(&res.Status))
}

// A DiscReq requests a connection to be terminated.
type DiscReq struct {
	Channel uint8
	Status  uint8
	Control HostInfo
}

// Service returns the service identifier for disconnect requests.
func (DiscReq) Service() ServiceID {
	return DiscReqService
}

// Size returns the packed size.
func (DiscReq) Size() uint {
	return 2 + hostInfoSize
}

// Pack the structure into the buffer.
func (req *DiscReq) Pack(buffer []byte) {
	buffer[0] = req.Channel
	buffer[1] = req.Status
	req.Control.Pack(buffer[2:])
}

// Unpack initializes the structure by parsing the given data.
func (req *DiscReq) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &req.Channel, &req.Status, &req.Control)
}

// A DiscRes is a response to a DiscReq..
type DiscRes struct {
	Channel uint8
	Status  uint8
}

// Service returns the service identifier for disconnect responses.
func (DiscRes) Service() ServiceID {
	return DiscResService
}

// Size returns the packed size.
func (DiscRes) Size() uint {
	return 2
}

// Pack the structure into the buffer.
func (res *DiscRes) Pack(data []byte) {
	data[0] = res.Channel
	data[1] = res.Status
}

// Unpack initializes the structure by parsing the given data.
func (res *DiscRes) Unpack(data []byte) (uint, error) {
	return util.UnpackSome(data, &res.Channel, &res.Status)
}
<|endoftext|>


<commit_before>package bbs

import "github.com/tedsuo/rata"

const (
	// Ping
	PingRoute_r0 = "Ping"

	// Domains
	DomainsRoute_r0      = "Domains"
	UpsertDomainRoute_r0 = "UpsertDomain"

	// Actual LRPs
	ActualLRPsRoute_r0                          = "ActualLRPs"
	ActualLRPGroupsRoute_r0                     = "ActualLRPGroups"
	ActualLRPGroupsByProcessGuidRoute_r0        = "ActualLRPGroupsByProcessGuid"
	ActualLRPGroupByProcessGuidAndIndexRoute_r0 = "ActualLRPGroupsByProcessGuidAndIndex"

	// Actual LRP Lifecycle
	ClaimActualLRPRoute_r0  = "ClaimActualLRP"
	StartActualLRPRoute_r0  = "StartActualLRP"
	CrashActualLRPRoute_r0  = "CrashActualLRP"
	FailActualLRPRoute_r0   = "FailActualLRP"
	RemoveActualLRPRoute_r0 = "RemoveActualLRP"
	RetireActualLRPRoute_r0 = "RetireActualLRP"

	// Evacuation
	RemoveEvacuatingActualLRPRoute_r0 = "RemoveEvacuatingActualLRP"
	EvacuateClaimedActualLRPRoute_r0  = "EvacuateClaimedActualLRP"
	EvacuateCrashedActualLRPRoute_r0  = "EvacuateCrashedActualLRP"
	EvacuateStoppedActualLRPRoute_r0  = "EvacuateStoppedActualLRP"
	EvacuateRunningActualLRPRoute_r0  = "EvacuateRunningActualLRP"

	// Desired LRPs
	DesiredLRPsRoute_r3               = "DesiredLRPs"
	DesiredLRPsRoute_r2               = "DesiredLRPs_r2"
	DesiredLRPSchedulingInfosRoute_r0 = "DesiredLRPSchedulingInfos"
	DesiredLRPByProcessGuidRoute_r3   = "DesiredLRPByProcessGuid"
	DesiredLRPByProcessGuidRoute_r2   = "DesiredLRPByProcessGuid_r2"

	// Desire LRP Lifecycle
	DesireDesiredLRPRoute_r2 = "DesireDesiredLRP"
	UpdateDesiredLRPRoute_r0 = "UpdateDesireLRP"
	RemoveDesiredLRPRoute    = "RemoveDesiredLRP"

	// Tasks
	TasksRoute_r2         = "Tasks_r2"
	TaskByGuidRoute_r2    = "TaskByGuid_r2"
	TasksRoute_r3         = "Tasks"
	TaskByGuidRoute_r3    = "TaskByGuid"
	DesireTaskRoute_r2    = "DesireTask"
	StartTaskRoute_r0     = "StartTask"
	CancelTaskRoute_r0    = "CancelTask"
	FailTaskRoute_r0      = "FailTask"
	RejectTaskRoute_r0    = "RejectTask"
	CompleteTaskRoute_r0  = "CompleteTask"
	ResolvingTaskRoute_r0 = "ResolvingTask"
	DeleteTaskRoute_r0    = "DeleteTask"

	// Event Streaming
	EventStreamRoute_r0            = "EventStream_r0"            // DEPRECATED
	TaskEventStreamRoute_r0        = "TaskEventStream_r0"        // DEPRECATED
	LrpInstanceEventStreamRoute_r0 = "LrpInstanceEventStream_r0" // DEPRECATED
	LRPGroupEventStreamRoute_r1    = "EventStream"
	TaskEventStreamRoute_r1        = "TaskEventStream"
	LRPInstanceEventStreamRoute_r1 = "LRPInstanceEventStream"

	// Cell Presence
	CellsRoute_r0 = "Cells"
)

var Routes = rata.Routes{
	// Ping
	{Path: "/v1/ping", Method: "POST", Name: PingRoute_r0},

	// Domains
	{Path: "/v1/domains/list", Method: "POST", Name: DomainsRoute_r0},
	{Path: "/v1/domains/upsert", Method: "POST", Name: UpsertDomainRoute_r0},

	// Actual LRPs
	{Path: "/v1/actual_lrps/list", Method: "POST", Name: ActualLRPsRoute_r0},
	{Path: "/v1/actual_lrp_groups/list", Method: "POST", Name: ActualLRPGroupsRoute_r0},
	{Path: "/v1/actual_lrp_groups/list_by_process_guid", Method: "POST", Name: ActualLRPGroupsByProcessGuidRoute_r0},
	{Path: "/v1/actual_lrp_groups/get_by_process_guid_and_index", Method: "POST", Name: ActualLRPGroupByProcessGuidAndIndexRoute_r0},

	// Actual LRP Lifecycle
	{Path: "/v1/actual_lrps/claim", Method: "POST", Name: ClaimActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/start", Method: "POST", Name: StartActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/crash", Method: "POST", Name: CrashActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/fail", Method: "POST", Name: FailActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/remove", Method: "POST", Name: RemoveActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/retire", Method: "POST", Name: RetireActualLRPRoute_r0},

	// Evacuation
	{Path: "/v1/actual_lrps/remove_evacuating", Method: "POST", Name: RemoveEvacuatingActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_claimed", Method: "POST", Name: EvacuateClaimedActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_crashed", Method: "POST", Name: EvacuateCrashedActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_stopped", Method: "POST", Name: EvacuateStoppedActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_running", Method: "POST", Name: EvacuateRunningActualLRPRoute_r0},

	// Desired LRPs
	{Path: "/v1/desired_lrp_scheduling_infos/list", Method: "POST", Name: DesiredLRPSchedulingInfosRoute_r0},

	{Path: "/v1/desired_lrps/list.r2", Method: "POST", Name: DesiredLRPsRoute_r2},                            // DEPRECATED
	{Path: "/v1/desired_lrps/get_by_process_guid.r2", Method: "POST", Name: DesiredLRPByProcessGuidRoute_r2}, // DEPRECATED
	{Path: "/v1/desired_lrps/list.r3", Method: "POST", Name: DesiredLRPsRoute_r3},
	{Path: "/v1/desired_lrps/get_by_process_guid.r3", Method: "POST", Name: DesiredLRPByProcessGuidRoute_r3},

	// Desire LPR Lifecycle
	{Path: "/v1/desired_lrp/desire.r2", Method: "POST", Name: DesireDesiredLRPRoute_r2},
	{Path: "/v1/desired_lrp/update", Method: "POST", Name: UpdateDesiredLRPRoute_r0},
	{Path: "/v1/desired_lrp/remove", Method: "POST", Name: RemoveDesiredLRPRoute},

	// Tasks
	{Path: "/v1/tasks/list.r2", Method: "POST", Name: TasksRoute_r2},                  // DEPRECATED
	{Path: "/v1/tasks/get_by_task_guid.r2", Method: "POST", Name: TaskByGuidRoute_r2}, // DEPRECATED
	{Path: "/v1/tasks/list.r3", Method: "POST", Name: TasksRoute_r3},
	{Path: "/v1/tasks/get_by_task_guid.r3", Method: "POST", Name: TaskByGuidRoute_r3},

	// Task Lifecycle
	{Path: "/v1/tasks/desire.r2", Method: "POST", Name: DesireTaskRoute_r2},
	{Path: "/v1/tasks/start", Method: "POST", Name: StartTaskRoute_r0},
	{Path: "/v1/tasks/cancel", Method: "POST", Name: CancelTaskRoute_r0},
	{Path: "/v1/tasks/fail", Method: "POST", Name: FailTaskRoute_r0},
	{Path: "/v1/tasks/reject", Method: "POST", Name: RejectTaskRoute_r0},
	{Path: "/v1/tasks/complete", Method: "POST", Name: CompleteTaskRoute_r0},
	{Path: "/v1/tasks/resolving", Method: "POST", Name: ResolvingTaskRoute_r0},
	{Path: "/v1/tasks/delete", Method: "POST", Name: DeleteTaskRoute_r0},

	// Event Streaming
	{Path: "/v1/events", Method: "GET", Name: EventStreamRoute_r0},
	{Path: "/v1/events/tasks", Method: "POST", Name: TaskEventStreamRoute_r0},
	{Path: "/v1/events/lrp_instances", Method: "POST", Name: LrpInstanceEventStreamRoute_r0},
	{Path: "/v1/events.r1", Method: "GET", Name: LRPGroupEventStreamRoute_r1},
	{Path: "/v1/events/tasks.r1", Method: "POST", Name: TaskEventStreamRoute_r1},
	{Path: "/v1/events/lrp_instances.r1", Method: "POST", Name: LRPInstanceEventStreamRoute_r1},

	// Cells
	{Path: "/v1/cells/list.r1", Method: "POST", Name: CellsRoute_r0},
}
<commit_msg>Add deprecation comments to all route constants and entries<commit_after>package bbs

import "github.com/tedsuo/rata"

const (
	// Ping
	PingRoute_r0 = "Ping"

	// Domains
	DomainsRoute_r0      = "Domains"
	UpsertDomainRoute_r0 = "UpsertDomain"

	// Actual LRPs
	ActualLRPsRoute_r0                          = "ActualLRPs"
	ActualLRPGroupsRoute_r0                     = "ActualLRPGroups"
	ActualLRPGroupsByProcessGuidRoute_r0        = "ActualLRPGroupsByProcessGuid"
	ActualLRPGroupByProcessGuidAndIndexRoute_r0 = "ActualLRPGroupsByProcessGuidAndIndex"

	// Actual LRP Lifecycle
	ClaimActualLRPRoute_r0  = "ClaimActualLRP"
	StartActualLRPRoute_r0  = "StartActualLRP"
	CrashActualLRPRoute_r0  = "CrashActualLRP"
	FailActualLRPRoute_r0   = "FailActualLRP"
	RemoveActualLRPRoute_r0 = "RemoveActualLRP"
	RetireActualLRPRoute_r0 = "RetireActualLRP"

	// Evacuation
	RemoveEvacuatingActualLRPRoute_r0 = "RemoveEvacuatingActualLRP"
	EvacuateClaimedActualLRPRoute_r0  = "EvacuateClaimedActualLRP"
	EvacuateCrashedActualLRPRoute_r0  = "EvacuateCrashedActualLRP"
	EvacuateStoppedActualLRPRoute_r0  = "EvacuateStoppedActualLRP"
	EvacuateRunningActualLRPRoute_r0  = "EvacuateRunningActualLRP"

	// Desired LRPs
	DesiredLRPsRoute_r3               = "DesiredLRPs"
	DesiredLRPSchedulingInfosRoute_r0 = "DesiredLRPSchedulingInfos"
	DesiredLRPByProcessGuidRoute_r3   = "DesiredLRPByProcessGuid"
	DesiredLRPsRoute_r2               = "DesiredLRPs_r2"             // DEPRECATED
	DesiredLRPByProcessGuidRoute_r2   = "DesiredLRPByProcessGuid_r2" // DEPRECATED

	// Desire LRP Lifecycle
	DesireDesiredLRPRoute_r2 = "DesireDesiredLRP"
	UpdateDesiredLRPRoute_r0 = "UpdateDesireLRP"
	RemoveDesiredLRPRoute    = "RemoveDesiredLRP"

	// Tasks
	TasksRoute_r3         = "Tasks"
	TaskByGuidRoute_r3    = "TaskByGuid"
	DesireTaskRoute_r2    = "DesireTask"
	StartTaskRoute_r0     = "StartTask"
	CancelTaskRoute_r0    = "CancelTask"
	FailTaskRoute_r0      = "FailTask"
	RejectTaskRoute_r0    = "RejectTask"
	CompleteTaskRoute_r0  = "CompleteTask"
	ResolvingTaskRoute_r0 = "ResolvingTask"
	DeleteTaskRoute_r0    = "DeleteTask"
	TasksRoute_r2         = "Tasks_r2"      // DEPRECATED
	TaskByGuidRoute_r2    = "TaskByGuid_r2" // DEPRECATED

	// Event Streaming
	LRPGroupEventStreamRoute_r1    = "EventStream"
	TaskEventStreamRoute_r1        = "TaskEventStream"
	LRPInstanceEventStreamRoute_r1 = "LRPInstanceEventStream"
	EventStreamRoute_r0            = "EventStream_r0"            // DEPRECATED
	TaskEventStreamRoute_r0        = "TaskEventStream_r0"        // DEPRECATED
	LrpInstanceEventStreamRoute_r0 = "LrpInstanceEventStream_r0" // DEPRECATED

	// Cell Presence
	CellsRoute_r0 = "Cells"
)

var Routes = rata.Routes{
	// Ping
	{Path: "/v1/ping", Method: "POST", Name: PingRoute_r0},

	// Domains
	{Path: "/v1/domains/list", Method: "POST", Name: DomainsRoute_r0},
	{Path: "/v1/domains/upsert", Method: "POST", Name: UpsertDomainRoute_r0},

	// Actual LRPs
	{Path: "/v1/actual_lrps/list", Method: "POST", Name: ActualLRPsRoute_r0},
	{Path: "/v1/actual_lrp_groups/list", Method: "POST", Name: ActualLRPGroupsRoute_r0},
	{Path: "/v1/actual_lrp_groups/list_by_process_guid", Method: "POST", Name: ActualLRPGroupsByProcessGuidRoute_r0},
	{Path: "/v1/actual_lrp_groups/get_by_process_guid_and_index", Method: "POST", Name: ActualLRPGroupByProcessGuidAndIndexRoute_r0},

	// Actual LRP Lifecycle
	{Path: "/v1/actual_lrps/claim", Method: "POST", Name: ClaimActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/start", Method: "POST", Name: StartActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/crash", Method: "POST", Name: CrashActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/fail", Method: "POST", Name: FailActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/remove", Method: "POST", Name: RemoveActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/retire", Method: "POST", Name: RetireActualLRPRoute_r0},

	// Evacuation
	{Path: "/v1/actual_lrps/remove_evacuating", Method: "POST", Name: RemoveEvacuatingActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_claimed", Method: "POST", Name: EvacuateClaimedActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_crashed", Method: "POST", Name: EvacuateCrashedActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_stopped", Method: "POST", Name: EvacuateStoppedActualLRPRoute_r0},
	{Path: "/v1/actual_lrps/evacuate_running", Method: "POST", Name: EvacuateRunningActualLRPRoute_r0},

	// Desired LRPs
	{Path: "/v1/desired_lrp_scheduling_infos/list", Method: "POST", Name: DesiredLRPSchedulingInfosRoute_r0},

	{Path: "/v1/desired_lrps/list.r3", Method: "POST", Name: DesiredLRPsRoute_r3},
	{Path: "/v1/desired_lrps/get_by_process_guid.r3", Method: "POST", Name: DesiredLRPByProcessGuidRoute_r3},
	{Path: "/v1/desired_lrps/list.r2", Method: "POST", Name: DesiredLRPsRoute_r2},                            // DEPRECATED
	{Path: "/v1/desired_lrps/get_by_process_guid.r2", Method: "POST", Name: DesiredLRPByProcessGuidRoute_r2}, // DEPRECATED

	// Desire LPR Lifecycle
	{Path: "/v1/desired_lrp/desire.r2", Method: "POST", Name: DesireDesiredLRPRoute_r2},
	{Path: "/v1/desired_lrp/update", Method: "POST", Name: UpdateDesiredLRPRoute_r0},
	{Path: "/v1/desired_lrp/remove", Method: "POST", Name: RemoveDesiredLRPRoute},

	// Tasks
	{Path: "/v1/tasks/list.r3", Method: "POST", Name: TasksRoute_r3},
	{Path: "/v1/tasks/get_by_task_guid.r3", Method: "POST", Name: TaskByGuidRoute_r3},
	{Path: "/v1/tasks/list.r2", Method: "POST", Name: TasksRoute_r2},                  // DEPRECATED
	{Path: "/v1/tasks/get_by_task_guid.r2", Method: "POST", Name: TaskByGuidRoute_r2}, // DEPRECATED

	// Task Lifecycle
	{Path: "/v1/tasks/desire.r2", Method: "POST", Name: DesireTaskRoute_r2},
	{Path: "/v1/tasks/start", Method: "POST", Name: StartTaskRoute_r0},
	{Path: "/v1/tasks/cancel", Method: "POST", Name: CancelTaskRoute_r0},
	{Path: "/v1/tasks/fail", Method: "POST", Name: FailTaskRoute_r0},
	{Path: "/v1/tasks/reject", Method: "POST", Name: RejectTaskRoute_r0},
	{Path: "/v1/tasks/complete", Method: "POST", Name: CompleteTaskRoute_r0},
	{Path: "/v1/tasks/resolving", Method: "POST", Name: ResolvingTaskRoute_r0},
	{Path: "/v1/tasks/delete", Method: "POST", Name: DeleteTaskRoute_r0},

	// Event Streaming
	{Path: "/v1/events.r1", Method: "GET", Name: LRPGroupEventStreamRoute_r1},
	{Path: "/v1/events/tasks.r1", Method: "POST", Name: TaskEventStreamRoute_r1},
	{Path: "/v1/events/lrp_instances.r1", Method: "POST", Name: LRPInstanceEventStreamRoute_r1},
	{Path: "/v1/events", Method: "GET", Name: EventStreamRoute_r0},                           // DEPRECATED
	{Path: "/v1/events/tasks", Method: "POST", Name: TaskEventStreamRoute_r0},                // DEPRECATED
	{Path: "/v1/events/lrp_instances", Method: "POST", Name: LrpInstanceEventStreamRoute_r0}, // DEPRECATED

	// Cells
	{Path: "/v1/cells/list.r1", Method: "POST", Name: CellsRoute_r0},
}
<|endoftext|>


<commit_before>package go_ginkgo

type RecentlyUsedList struct {
	list  [10]string
	count int
}

func (r *RecentlyUsedList) IsEmpty() bool {
	if r.count == 0 {
		return true
	} else {
		return false
	}
}

func (r *RecentlyUsedList) Push(str string) {
	if r.Index(str) == -1 {
		r.list[r.count] = str
		r.count++
	}
}

func (r *RecentlyUsedList) Fetch(index int) string {
	return r.list[r.count-1-index]
}

func (r *RecentlyUsedList) Count() int {
	return r.count
}

func (r *RecentlyUsedList) Index(str string) int {
	i := 0
	for i < r.count {
		if r.Fetch(i) == str {
			return i
		}
		i++
	}
	return -1
}
<commit_msg>リファクタリング<commit_after>package go_ginkgo

type RecentlyUsedList struct {
	list  [10]string
	count int
}

func (r *RecentlyUsedList) IsEmpty() bool {
	return r.count == 0
}

func (r *RecentlyUsedList) Push(str string) {
	if r.Index(str) == -1 {
		r.list[r.count] = str
		r.count++
	}
}

func (r *RecentlyUsedList) Fetch(index int) string {
	return r.list[r.count-1-index]
}

func (r *RecentlyUsedList) Count() int {
	return r.count
}

func (r *RecentlyUsedList) Index(str string) int {
	for i := 0; i < r.count; i++ {
		if r.Fetch(i) == str {
			return i
		}
	}
	return -1
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"code.google.com/p/go.tools/godoc/static"
	"code.google.com/p/go.tools/present"
)

var (
	uiContent      []byte
	lessons        = make(map[string][]byte)
	lessonNotFound = fmt.Errorf("lesson not found")
)

// initTour loads tour.article and the relevant HTML templates from the given
// tour root, and renders the template to the tourContent global variable.
func initTour(root, transport string) error {
	// Make sure playground is enabled before rendering.
	present.PlayEnabled = true

	// Set up templates.
	action := filepath.Join(root, "template", "action.tmpl")
	tmpl, err := present.Template().ParseFiles(action)
	if err != nil {
		return fmt.Errorf("parse templates: %v", err)
	}

	// Init lessons.
	contentPath := filepath.Join(root, "content")
	if err := initLessons(tmpl, contentPath); err != nil {
		return fmt.Errorf("init lessons: %v", err)
	}

	// Init UI
	index := filepath.Join(root, "template", "index.tmpl")
	ui, err := template.ParseFiles(index)
	if err != nil {
		return fmt.Errorf("parse index.tmpl: %v", err)
	}
	buf := new(bytes.Buffer)

	data := struct {
		SocketAddr string
		Transport  template.JS
	}{socketAddr(), template.JS(transport)}

	if err := ui.Execute(buf, data); err != nil {
		return fmt.Errorf("render UI: %v", err)
	}
	uiContent = buf.Bytes()

	return initScript(root)
}

// initLessonss finds all the lessons in the passed directory, renders them,
// using the given template and saves the content in the lessons map.
func initLessons(tmpl *template.Template, content string) error {
	dir, err := os.Open(content)
	if err != nil {
		return err
	}
	files, err := dir.Readdirnames(0)
	if err != nil {
		return err
	}
	for _, f := range files {
		if filepath.Ext(f) != ".article" {
			continue
		}
		content, err := parseLesson(tmpl, filepath.Join(content, f))
		if err != nil {
			return fmt.Errorf("parsing %v: %v", f, err)
		}
		name := strings.TrimSuffix(f, ".article")
		lessons[name] = content
	}
	return nil
}

// File defines the JSON form of a code file in a page.
type File struct {
	Name    string
	Content string
}

// Page defines the JSON form of a tour lesson page.
type Page struct {
	Title   string
	Content string
	Files   []File
}

// Lesson defines the JSON form of a tour lesson.
type Lesson struct {
	Title       string
	Description string
	Pages       []Page
}

// parseLesson parses and returns a lesson content given its name and
// the template to render it.
func parseLesson(tmpl *template.Template, path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	doc, err := present.Parse(prepContent(f), path, 0)
	if err != nil {
		return nil, err
	}

	lesson := Lesson{
		doc.Title,
		doc.Subtitle,
		make([]Page, len(doc.Sections)),
	}

	for i, sec := range doc.Sections {
		p := &lesson.Pages[i]
		w := new(bytes.Buffer)
		if err := sec.Render(w, tmpl); err != nil {
			return nil, fmt.Errorf("render section: %v", err)
		}
		p.Title = sec.Title
		p.Content = w.String()
		codes := findPlayCode(sec)
		p.Files = make([]File, len(codes))
		for i, c := range codes {
			f := &p.Files[i]
			f.Name = c.FileName
			f.Content = string(c.Raw)
		}
	}

	w := new(bytes.Buffer)
	if err := json.NewEncoder(w).Encode(lesson); err != nil {
		return nil, fmt.Errorf("encode lesson: %v", err)
	}
	return w.Bytes(), nil
}

// findPlayCode returns a slide with all the Code elements in the given
// Elem with Play set to true.
func findPlayCode(e present.Elem) []*present.Code {
	var r []*present.Code
	switch v := e.(type) {
	case present.Code:
		if v.Play {
			r = append(r, &v)
		}
	case present.Section:
		for _, s := range v.Elem {
			r = append(r, findPlayCode(s)...)
		}
	}
	return r
}

// writeLesson writes the tour content to the provided Writer.
func writeLesson(name string, w io.Writer) error {
	if uiContent == nil {
		panic("writeLesson called before successful initTour")
	}
	if len(name) == 0 {
		return writeAllLessons(w)
	}
	l, ok := lessons[name]
	if !ok {
		return lessonNotFound
	}
	_, err := w.Write(l)
	return err
}

func writeAllLessons(w io.Writer) error {
	if _, err := fmt.Fprint(w, "{"); err != nil {
		return err
	}
	nLessons := len(lessons)
	for k, v := range lessons {
		if _, err := fmt.Fprintf(w, "%q:%s", k, v); err != nil {
			return err
		}
		nLessons--
		if nLessons != 0 {
			if _, err := fmt.Fprint(w, ","); err != nil {
				return err
			}
		}
	}
	_, err := fmt.Fprint(w, "}")
	return err
}

// renderUI writes the tour UI to the provided Writer.
func renderUI(w io.Writer) error {
	if uiContent == nil {
		panic("renderUI called before successful initTour")
	}
	_, err := w.Write(uiContent)
	return err
}

// nocode returns true if the provided Section contains
// no Code elements with Play enabled.
func nocode(s present.Section) bool {
	for _, e := range s.Elem {
		if c, ok := e.(present.Code); ok && c.Play {
			return false
		}
	}
	return true
}

// initScript concatenates all the javascript files needed to render
// the tour UI and serves the result on /script.js.
func initScript(root string) error {
	modTime := time.Now()
	b := new(bytes.Buffer)

	content, ok := static.Files["playground.js"]
	if !ok {
		return fmt.Errorf("playground.js not found in static files")
	}
	b.WriteString(content)

	// Keep this list in dependency order
	files := []string{
		"static/lib/jquery.min.js",
		"static/lib/jquery-ui.min.js",
		"static/lib/angular.min.js",
		"static/lib/codemirror/lib/codemirror.js",
		"static/lib/codemirror/addon/edit/matchbrackets.js",
		"static/lib/codemirror/mode/go/go.js",
		"static/lib/angular-ui.min.js",
		"static/js/app.js",
		"static/js/controllers.js",
		"static/js/directives.js",
		"static/js/services.js",
		"static/js/values.js",
	}

	for _, file := range files {
		f, err := ioutil.ReadFile(filepath.Join(root, file))
		if err != nil {
			return fmt.Errorf("couldn't open %v", file, err)
		}
		_, err = b.Write(f)
		if err != nil {
			return fmt.Errorf("error concatenating %v", file, err)
		}
	}

	http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-type", "application/javascript")
		// Set expiration time in one week.
		w.Header().Set("Cache-control", "max-age=604800")
		http.ServeContent(w, r, "", modTime, bytes.NewReader(b.Bytes()))
	})

	return nil
}
<commit_msg>[x/tour] go-tour: fix a couple of bad format strings<commit_after>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"code.google.com/p/go.tools/godoc/static"
	"code.google.com/p/go.tools/present"
)

var (
	uiContent      []byte
	lessons        = make(map[string][]byte)
	lessonNotFound = fmt.Errorf("lesson not found")
)

// initTour loads tour.article and the relevant HTML templates from the given
// tour root, and renders the template to the tourContent global variable.
func initTour(root, transport string) error {
	// Make sure playground is enabled before rendering.
	present.PlayEnabled = true

	// Set up templates.
	action := filepath.Join(root, "template", "action.tmpl")
	tmpl, err := present.Template().ParseFiles(action)
	if err != nil {
		return fmt.Errorf("parse templates: %v", err)
	}

	// Init lessons.
	contentPath := filepath.Join(root, "content")
	if err := initLessons(tmpl, contentPath); err != nil {
		return fmt.Errorf("init lessons: %v", err)
	}

	// Init UI
	index := filepath.Join(root, "template", "index.tmpl")
	ui, err := template.ParseFiles(index)
	if err != nil {
		return fmt.Errorf("parse index.tmpl: %v", err)
	}
	buf := new(bytes.Buffer)

	data := struct {
		SocketAddr string
		Transport  template.JS
	}{socketAddr(), template.JS(transport)}

	if err := ui.Execute(buf, data); err != nil {
		return fmt.Errorf("render UI: %v", err)
	}
	uiContent = buf.Bytes()

	return initScript(root)
}

// initLessonss finds all the lessons in the passed directory, renders them,
// using the given template and saves the content in the lessons map.
func initLessons(tmpl *template.Template, content string) error {
	dir, err := os.Open(content)
	if err != nil {
		return err
	}
	files, err := dir.Readdirnames(0)
	if err != nil {
		return err
	}
	for _, f := range files {
		if filepath.Ext(f) != ".article" {
			continue
		}
		content, err := parseLesson(tmpl, filepath.Join(content, f))
		if err != nil {
			return fmt.Errorf("parsing %v: %v", f, err)
		}
		name := strings.TrimSuffix(f, ".article")
		lessons[name] = content
	}
	return nil
}

// File defines the JSON form of a code file in a page.
type File struct {
	Name    string
	Content string
}

// Page defines the JSON form of a tour lesson page.
type Page struct {
	Title   string
	Content string
	Files   []File
}

// Lesson defines the JSON form of a tour lesson.
type Lesson struct {
	Title       string
	Description string
	Pages       []Page
}

// parseLesson parses and returns a lesson content given its name and
// the template to render it.
func parseLesson(tmpl *template.Template, path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	doc, err := present.Parse(prepContent(f), path, 0)
	if err != nil {
		return nil, err
	}

	lesson := Lesson{
		doc.Title,
		doc.Subtitle,
		make([]Page, len(doc.Sections)),
	}

	for i, sec := range doc.Sections {
		p := &lesson.Pages[i]
		w := new(bytes.Buffer)
		if err := sec.Render(w, tmpl); err != nil {
			return nil, fmt.Errorf("render section: %v", err)
		}
		p.Title = sec.Title
		p.Content = w.String()
		codes := findPlayCode(sec)
		p.Files = make([]File, len(codes))
		for i, c := range codes {
			f := &p.Files[i]
			f.Name = c.FileName
			f.Content = string(c.Raw)
		}
	}

	w := new(bytes.Buffer)
	if err := json.NewEncoder(w).Encode(lesson); err != nil {
		return nil, fmt.Errorf("encode lesson: %v", err)
	}
	return w.Bytes(), nil
}

// findPlayCode returns a slide with all the Code elements in the given
// Elem with Play set to true.
func findPlayCode(e present.Elem) []*present.Code {
	var r []*present.Code
	switch v := e.(type) {
	case present.Code:
		if v.Play {
			r = append(r, &v)
		}
	case present.Section:
		for _, s := range v.Elem {
			r = append(r, findPlayCode(s)...)
		}
	}
	return r
}

// writeLesson writes the tour content to the provided Writer.
func writeLesson(name string, w io.Writer) error {
	if uiContent == nil {
		panic("writeLesson called before successful initTour")
	}
	if len(name) == 0 {
		return writeAllLessons(w)
	}
	l, ok := lessons[name]
	if !ok {
		return lessonNotFound
	}
	_, err := w.Write(l)
	return err
}

func writeAllLessons(w io.Writer) error {
	if _, err := fmt.Fprint(w, "{"); err != nil {
		return err
	}
	nLessons := len(lessons)
	for k, v := range lessons {
		if _, err := fmt.Fprintf(w, "%q:%s", k, v); err != nil {
			return err
		}
		nLessons--
		if nLessons != 0 {
			if _, err := fmt.Fprint(w, ","); err != nil {
				return err
			}
		}
	}
	_, err := fmt.Fprint(w, "}")
	return err
}

// renderUI writes the tour UI to the provided Writer.
func renderUI(w io.Writer) error {
	if uiContent == nil {
		panic("renderUI called before successful initTour")
	}
	_, err := w.Write(uiContent)
	return err
}

// nocode returns true if the provided Section contains
// no Code elements with Play enabled.
func nocode(s present.Section) bool {
	for _, e := range s.Elem {
		if c, ok := e.(present.Code); ok && c.Play {
			return false
		}
	}
	return true
}

// initScript concatenates all the javascript files needed to render
// the tour UI and serves the result on /script.js.
func initScript(root string) error {
	modTime := time.Now()
	b := new(bytes.Buffer)

	content, ok := static.Files["playground.js"]
	if !ok {
		return fmt.Errorf("playground.js not found in static files")
	}
	b.WriteString(content)

	// Keep this list in dependency order
	files := []string{
		"static/lib/jquery.min.js",
		"static/lib/jquery-ui.min.js",
		"static/lib/angular.min.js",
		"static/lib/codemirror/lib/codemirror.js",
		"static/lib/codemirror/addon/edit/matchbrackets.js",
		"static/lib/codemirror/mode/go/go.js",
		"static/lib/angular-ui.min.js",
		"static/js/app.js",
		"static/js/controllers.js",
		"static/js/directives.js",
		"static/js/services.js",
		"static/js/values.js",
	}

	for _, file := range files {
		f, err := ioutil.ReadFile(filepath.Join(root, file))
		if err != nil {
			return fmt.Errorf("couldn't open %v: %v", file, err)
		}
		_, err = b.Write(f)
		if err != nil {
			return fmt.Errorf("error concatenating %v: %v", file, err)
		}
	}

	http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-type", "application/javascript")
		// Set expiration time in one week.
		w.Header().Set("Cache-control", "max-age=604800")
		http.ServeContent(w, r, "", modTime, bytes.NewReader(b.Bytes()))
	})

	return nil
}
<|endoftext|>


<commit_before>package activitylog

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.skia.org/infra/go/ds"
	"go.skia.org/infra/go/ds/testutil"
	"go.skia.org/infra/go/testutils"
)

func TestActivity(t *testing.T) {
	testutils.LargeTest(t)
	cleanup := testutil.InitDatastore(t, ds.ACTIVITY)

	defer cleanup()

	// Add one activity.
	a := &Activity{
		UserID: "user@example.com",
		Action: "Triage",
	}
	err := Write(a)
	assert.NoError(t, err)

	// Confirm it's there.
	list, err := GetRecent(2)
	assert.NoError(t, err)
	assert.Len(t, list, 1)

	time.Sleep(3)

	// Add another item.
	a.UserID = "somebody@example.org"
	err = Write(a)
	assert.NoError(t, err)

	// Confirm they're both there.
	list, err = GetRecent(2)
	assert.NoError(t, err)
	assert.Len(t, list, 2)
	assert.Equal(t, "user@example.com", list[0].UserID)
	assert.Equal(t, "somebody@example.org", list[1].UserID)

	// Confirm GetRecent honors its argument.
	list, err = GetRecent(1)
	assert.NoError(t, err)
	assert.Len(t, list, 1)
	assert.Equal(t, "user@example.com", list[0].UserID)
}
<commit_msg>[perf] Make more tests local only.<commit_after>package activitylog

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.skia.org/infra/go/ds"
	"go.skia.org/infra/go/ds/testutil"
	"go.skia.org/infra/go/testutils"
)

func TestActivity(t *testing.T) {
	testutils.LargeTest(t)
	testutils.LocalOnlyTest(t)
	cleanup := testutil.InitDatastore(t, ds.ACTIVITY)

	defer cleanup()

	// Add one activity.
	a := &Activity{
		UserID: "user@example.com",
		Action: "Triage",
	}
	err := Write(a)
	assert.NoError(t, err)

	// Confirm it's there.
	list, err := GetRecent(2)
	assert.NoError(t, err)
	assert.Len(t, list, 1)

	time.Sleep(3)

	// Add another item.
	a.UserID = "somebody@example.org"
	err = Write(a)
	assert.NoError(t, err)

	// Confirm they're both there.
	list, err = GetRecent(2)
	assert.NoError(t, err)
	assert.Len(t, list, 2)
	assert.Equal(t, "user@example.com", list[0].UserID)
	assert.Equal(t, "somebody@example.org", list[1].UserID)

	// Confirm GetRecent honors its argument.
	list, err = GetRecent(1)
	assert.NoError(t, err)
	assert.Len(t, list, 1)
	assert.Equal(t, "user@example.com", list[0].UserID)
}
<|endoftext|>


<commit_before>package trace

import (
	"context"
	"github.com/shawnfeng/sutil/sconf/center"
	"github.com/shawnfeng/sutil/slog/slog"
	"net/http"
	"strings"
	"sync"
)

const (
	ServiceName = "trace"
	DefaultNameSpace = "application"
)

const FilterUrls = "span_filter_urls"

const ListConfigSep = ","

var apolloCenter center.ConfigCenter

var apolloSpanFilterConfig *spanFilterConfig

func init() {
	fun := "trace.init --> "
	ctx := context.Background()

	var err error
	apolloCenter, err = center.NewConfigCenter(center.ApolloConfigCenter)
	if err != nil {
		slog.Errorf(ctx, "%s new config center error, center type: %d, err: %s", fun, center.ApolloConfigCenter, err.Error())
		return
	}

	err = apolloCenter.Init(ctx, ServiceName, nil)
	if err != nil {
		slog.Errorf(ctx, "%s init apollo config center error, service name: %s, namespaces: %s", fun, ServiceName, "")
		return
	}

	urls, ok := apolloCenter.GetString(ctx, FilterUrls)
	if !ok {
		slog.Errorf(ctx, "%s get %s from apollo failed", fun, FilterUrls)
		return
	}
	slog.Infof(ctx, "%s get %s from apollo: %s", fun, FilterUrls, urls)

	urlList := strings.Split(urls, ListConfigSep)

	apolloSpanFilterConfig = &spanFilterConfig{
		urls: urlList,
	}

	apolloCenter.StartWatchUpdate(ctx)
	apolloCenter.RegisterObserver(ctx, apolloSpanFilterConfig)
}

type spanFilterConfig struct {
	mu sync.RWMutex

	urls []string
}

func (m *spanFilterConfig) updateUrls(urls []string) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.urls = urls
}

func (m *spanFilterConfig) filterUrl(url string) bool {
	m.mu.RLock()
	defer m.mu.RUnlock()

	for _, u := range m.urls {
		if u == url {
			return false
		}
	}

	return true
}

func (m *spanFilterConfig) HandleChangeEvent(event *center.ChangeEvent) {
	fun := "spanFilterConfig.HandleChangeEvent --> "
	ctx := context.Background()

	if event.Namespace != DefaultNameSpace {
		return
	}

	for key, change := range event.Changes {
		if key == FilterUrls {
			slog.Infof(ctx, "%s get key %s from apollo, old value: %s, new value: %s", fun, key, change.OldValue, change.NewValue)
			urlList := strings.Split(change.NewValue, ListConfigSep)
			m.updateUrls(urlList)
		}
	}
}

func UrlSpanFilter(r *http.Request) bool {
	if apolloSpanFilterConfig != nil {
		return apolloSpanFilterConfig.filterUrl(r.URL.Path)
	}

	return true
}
<commit_msg>Format spanfilter.go<commit_after>package trace

import (
	"context"
	"github.com/shawnfeng/sutil/sconf/center"
	"github.com/shawnfeng/sutil/slog/slog"
	"net/http"
	"strings"
	"sync"
)

const (
	ServiceName      = "trace"
	DefaultNameSpace = "application"
)

const FilterUrls = "span_filter_urls"

const ListConfigSep = ","

var apolloCenter center.ConfigCenter

var apolloSpanFilterConfig *spanFilterConfig

func init() {
	fun := "trace.init --> "
	ctx := context.Background()

	var err error
	apolloCenter, err = center.NewConfigCenter(center.ApolloConfigCenter)
	if err != nil {
		slog.Errorf(ctx, "%s new config center error, center type: %d, err: %s", fun, center.ApolloConfigCenter, err.Error())
		return
	}

	err = apolloCenter.Init(ctx, ServiceName, nil)
	if err != nil {
		slog.Errorf(ctx, "%s init apollo config center error, service name: %s, namespaces: %s", fun, ServiceName, "")
		return
	}

	urls, ok := apolloCenter.GetString(ctx, FilterUrls)
	if !ok {
		slog.Errorf(ctx, "%s get %s from apollo failed", fun, FilterUrls)
		return
	}
	slog.Infof(ctx, "%s get %s from apollo: %s", fun, FilterUrls, urls)

	urlList := strings.Split(urls, ListConfigSep)

	apolloSpanFilterConfig = &spanFilterConfig{
		urls: urlList,
	}

	apolloCenter.StartWatchUpdate(ctx)
	apolloCenter.RegisterObserver(ctx, apolloSpanFilterConfig)
}

type spanFilterConfig struct {
	mu sync.RWMutex

	urls []string
}

func (m *spanFilterConfig) updateUrls(urls []string) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.urls = urls
}

func (m *spanFilterConfig) filterUrl(url string) bool {
	m.mu.RLock()
	defer m.mu.RUnlock()

	for _, u := range m.urls {
		if u == url {
			return false
		}
	}

	return true
}

func (m *spanFilterConfig) HandleChangeEvent(event *center.ChangeEvent) {
	fun := "spanFilterConfig.HandleChangeEvent --> "
	ctx := context.Background()

	if event.Namespace != DefaultNameSpace {
		return
	}

	for key, change := range event.Changes {
		if key == FilterUrls {
			slog.Infof(ctx, "%s get key %s from apollo, old value: %s, new value: %s", fun, key, change.OldValue, change.NewValue)
			urlList := strings.Split(change.NewValue, ListConfigSep)
			m.updateUrls(urlList)
		}
	}
}

func UrlSpanFilter(r *http.Request) bool {
	if apolloSpanFilterConfig != nil {
		return apolloSpanFilterConfig.filterUrl(r.URL.Path)
	}

	return true
}
<|endoftext|>


<commit_before>package trace

import (
	"net/http"
	"testing"

	opentracing "github.com/opentracing/opentracing-go"
	mocktracer "github.com/opentracing/opentracing-go/mocktracer"
	zipkin "github.com/openzipkin/zipkin-go-opentracing"
	zipkintypes "github.com/openzipkin/zipkin-go-opentracing/types"
)

const testServiceName = "TEST-SERVICE"

func TestInjectHeaders(t *testing.T) {
	mt := mocktracer.New()
	opentracing.SetGlobalTracer(mt)
	globalTracer := opentracing.GlobalTracer()
	span := globalTracer.StartSpan(testServiceName)

	req, _ := http.NewRequest("GET", "http://example.com", nil)
	InjectHeaders(span, req)

	if req.Header.Get("Mockpfx-Ids-Traceid") == "" {
		t.Error("Inject did not set the Traceid in the request.")
		t.Fail()
	}
	if req.Header.Get("Mockpfx-Ids-Spanid") == "" {
		t.Error("Inject did not set the Spanid in the request.")
		t.Fail()
	}
}

func TestInjectHeadersWithParentSpan(t *testing.T) {
	parentSpanId := uint64(12345)
	parentSpanContext := zipkin.SpanContext{
		SpanID:  parentSpanId,
		TraceID: zipkintypes.TraceID{High: uint64(1234), Low: uint64(4321)},
	}

	tracer, _ := zipkin.NewTracer(nil)
	opentracing.SetGlobalTracer(tracer)
	globalTracer := opentracing.GlobalTracer()
	childSpan := globalTracer.StartSpan(testServiceName+"-CHILD", opentracing.ChildOf(parentSpanContext))

	req, _ := http.NewRequest("GET", "http://example.com", nil)
	InjectHeaders(childSpan, req)

	if req.Header.Get("X-B3-Traceid") == "" {
		t.Error("Inject did not set the Traceid in the request.")
		t.Fail()
	}
	if req.Header.Get("X-B3-Spanid") == "" {
		t.Error("Inject did not set the Spanid in the request.")
		t.Fail()
	}
	if req.Header.Get("X-B3-Parentspanid") != "0000000000003039" {
		t.Error("Inject did not set the correct Parentspanid in the request.")
		t.Fail()
	}
	if req.Header.Get("x-B3-Traceid") != "00000000000004d200000000000010e1" {
		t.Error("Inject did not reuse the Traceid from the parent span")
		t.Fail()
	}
}
<commit_msg>Added tests for trace.CreateSpan.<commit_after>package trace

import (
	"net/http"
	"testing"

	opentracing "github.com/opentracing/opentracing-go"
	mocktracer "github.com/opentracing/opentracing-go/mocktracer"
	zipkin "github.com/openzipkin/zipkin-go-opentracing"
	zipkintypes "github.com/openzipkin/zipkin-go-opentracing/types"
)

const testServiceName = "TEST-SERVICE"

func TestCreateSpanNoGlobalTracer(t *testing.T) {
	opentracing.SetGlobalTracer(nil)
	req, _ := http.NewRequest("GET", "http://example.com", nil)

	if CreateSpan(req, testServiceName) != nil {
		t.Error("CreateSpan returned a non-nil result using a nil global tracer.")
		t.Fail()
	}
}

func TestCreateSpanWithNoParent(t *testing.T) {
	tracer, _ := zipkin.NewTracer(nil)
	opentracing.SetGlobalTracer(tracer)

	req, _ := http.NewRequest("GET", "http://example.com", nil)
	if CreateSpan(req, testServiceName) == nil {
		t.Error("Received nil span while a global tracer was set.")
		t.FailNow()
	}
}

func TestCreateSpanWithParent(t *testing.T) {
	mt := mocktracer.New()
	opentracing.SetGlobalTracer(mt)
	globalTracer := opentracing.GlobalTracer()
	parentSpan := globalTracer.StartSpan(testServiceName)

	requestIn, _ := http.NewRequest("GET", "http://example.com", nil)
	mt.Inject(parentSpan.Context(),
		opentracing.HTTPHeaders,
		opentracing.HTTPHeadersCarrier(requestIn.Header),
	)

	if CreateSpan(requestIn, testServiceName+"-child") == nil {
		t.Error("Received a nil span while a global tracer was set.")
		t.FailNow()
	}
}

func TestInjectHeaders(t *testing.T) {
	mt := mocktracer.New()
	opentracing.SetGlobalTracer(mt)
	globalTracer := opentracing.GlobalTracer()
	span := globalTracer.StartSpan(testServiceName)

	req, _ := http.NewRequest("GET", "http://example.com", nil)
	InjectHeaders(span, req)

	if req.Header.Get("Mockpfx-Ids-Traceid") == "" {
		t.Error("Inject did not set the Traceid in the request.")
		t.Fail()
	}
	if req.Header.Get("Mockpfx-Ids-Spanid") == "" {
		t.Error("Inject did not set the Spanid in the request.")
		t.Fail()
	}
}

func TestInjectHeadersWithParentSpan(t *testing.T) {
	parentSpanId := uint64(12345)
	parentSpanContext := zipkin.SpanContext{
		SpanID:  parentSpanId,
		TraceID: zipkintypes.TraceID{High: uint64(1234), Low: uint64(4321)},
	}

	tracer, _ := zipkin.NewTracer(nil)
	opentracing.SetGlobalTracer(tracer)
	globalTracer := opentracing.GlobalTracer()
	childSpan := globalTracer.StartSpan(testServiceName+"-CHILD", opentracing.ChildOf(parentSpanContext))

	req, _ := http.NewRequest("GET", "http://example.com", nil)
	InjectHeaders(childSpan, req)

	if req.Header.Get("X-B3-Traceid") == "" {
		t.Error("Inject did not set the Traceid in the request.")
		t.Fail()
	}
	if req.Header.Get("X-B3-Spanid") == "" {
		t.Error("Inject did not set the Spanid in the request.")
		t.Fail()
	}
	if req.Header.Get("X-B3-Parentspanid") != "0000000000003039" {
		t.Error("Inject did not set the correct Parentspanid in the request.")
		t.Fail()
	}
	if req.Header.Get("x-B3-Traceid") != "00000000000004d200000000000010e1" {
		t.Error("Inject did not reuse the Traceid from the parent span")
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package helm

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"time"

	"github.com/rancher/rancher/pkg/controllers/management/compose/common"
	"github.com/rancher/rancher/pkg/ref"
	corev1 "github.com/rancher/types/apis/core/v1"
	mgmtv3 "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/rancher/types/user"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	helmTokenPrefix = "helm-token-"
	description     = "token for helm chart deployment"
	AppIDsLabel     = "cattle.io/appIds"
)

func Register(ctx context.Context, user *config.UserContext, kubeConfigGetter common.KubeConfigGetter) {
	appClient := user.Management.Project.Apps("")
	stackLifecycle := &Lifecycle{
		KubeConfigGetter:      kubeConfigGetter,
		TokenClient:           user.Management.Management.Tokens(""),
		UserClient:            user.Management.Management.Users(""),
		UserManager:           user.Management.UserManager,
		K8sClient:             user.K8sClient,
		TemplateVersionClient: user.Management.Management.TemplateVersions(""),
		ListenConfigClient:    user.Management.Management.ListenConfigs(""),
		ClusterName:           user.ClusterName,
		TemplateContentClient: user.Management.Management.TemplateContents(""),
		AppRevisionGetter:     user.Management.Project,
		AppGetter:             user.Management.Project,
		NsClient:              user.Core.Namespaces(""),
	}
	appClient.AddClusterScopedLifecycle(ctx, "helm-controller", user.ClusterName, stackLifecycle)
}

type Lifecycle struct {
	KubeConfigGetter      common.KubeConfigGetter
	UserManager           user.Manager
	TokenClient           mgmtv3.TokenInterface
	UserClient            mgmtv3.UserInterface
	TemplateVersionClient mgmtv3.TemplateVersionInterface
	K8sClient             kubernetes.Interface
	ListenConfigClient    mgmtv3.ListenConfigInterface
	ClusterName           string
	TemplateContentClient mgmtv3.TemplateContentInterface
	AppRevisionGetter     v3.AppRevisionsGetter
	AppGetter             v3.AppsGetter
	NsClient              corev1.NamespaceInterface
}

func (l *Lifecycle) Create(obj *v3.App) (runtime.Object, error) {
	v3.AppConditionMigrated.True(obj)
	return obj, nil
}

func (l *Lifecycle) Updated(obj *v3.App) (runtime.Object, error) {
	if obj.Spec.ExternalID == "" && len(obj.Spec.Files) == 0 {
		return obj, nil
	}
	// always refresh app to avoid updating app twice
	_, projectName := ref.Parse(obj.Spec.ProjectName)
	var err error
	obj, err = l.AppGetter.Apps(projectName).Get(obj.Name, metav1.GetOptions{})
	if err != nil {
		return obj, err
	}
	// if app was created before 2.1, run migrate to install a no-op helm release
	newObj, err := v3.AppConditionMigrated.Once(obj, func() (runtime.Object, error) {
		return l.DeployApp(obj)
	})
	if err != nil {
		return obj, err
	}
	obj = newObj.(*v3.App)
	appRevisionClient := l.AppRevisionGetter.AppRevisions(projectName)
	if obj.Spec.AppRevisionName != "" {
		currentRevision, err := appRevisionClient.Get(obj.Spec.AppRevisionName, metav1.GetOptions{})
		if err != nil {
			return nil, err
		}
		if obj.Spec.ExternalID != "" {
			if currentRevision.Status.ExternalID == obj.Spec.ExternalID && reflect.DeepEqual(currentRevision.Status.Answers, obj.Spec.Answers) {
				return obj, nil
			}
		}
		if obj.Status.AppliedFiles != nil {
			if reflect.DeepEqual(obj.Status.AppliedFiles, obj.Spec.Files) && reflect.DeepEqual(currentRevision.Status.Answers, obj.Spec.Answers) {
				return obj, nil
			}
		}
	}
	result, err := l.DeployApp(obj)
	if err != nil {
		return result, err
	}
	ns, err := l.NsClient.Get(obj.Spec.TargetNamespace, metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return result, err
	} else if errors.IsNotFound(err) {
		return result, nil
	}
	if ns.Annotations[AppIDsLabel] == "" {
		ns.Annotations[AppIDsLabel] = obj.Name
	} else {
		parts := strings.Split(ns.Annotations[AppIDsLabel], ",")
		appIDs := map[string]struct{}{}
		for _, part := range parts {
			appIDs[part] = struct{}{}
		}
		appIDs[obj.Name] = struct{}{}
		appIDList := []string{}
		for k := range appIDs {
			appIDList = append(appIDList, k)
		}
		sort.Strings(appIDList)
		ns.Annotations[AppIDsLabel] = strings.Join(appIDList, ",")
	}
	if _, err := l.NsClient.Update(ns); err != nil {
		return result, err
	}
	return result, nil
}

func (l *Lifecycle) DeployApp(obj *v3.App) (*v3.App, error) {
	newObj, err := v3.AppConditionInstalled.Do(obj, func() (runtime.Object, error) {
		template, notes, tempDir, err := generateTemplates(obj, l.TemplateVersionClient, l.TemplateContentClient)
		defer os.RemoveAll(tempDir)
		if err != nil {
			return obj, err
		}
		if err := l.Run(obj, template, tempDir, notes); err != nil {
			return obj, err
		}
		return obj, nil
	})
	return newObj.(*v3.App), err
}

func (l *Lifecycle) Remove(obj *v3.App) (runtime.Object, error) {
	_, projectName := ref.Parse(obj.Spec.ProjectName)
	appRevisionClient := l.AppRevisionGetter.AppRevisions(projectName)
	revisions, err := appRevisionClient.List(metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", appLabel, obj.Name),
	})
	if err != nil {
		return obj, err
	}
	for _, revision := range revisions.Items {
		if err := appRevisionClient.Delete(revision.Name, &metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
			return obj, err
		}
	}
	tempDir, err := ioutil.TempDir("", "helm-")
	if err != nil {
		return obj, err
	}
	defer os.RemoveAll(tempDir)
	kubeConfigPath, err := l.writeKubeConfig(obj, tempDir)
	if err != nil {
		return obj, err
	}
	// try three times and succeed
	start := time.Second * 1
	for i := 0; i < 3; i++ {
		if err := helmDelete(kubeConfigPath, obj); err == nil {
			break
		}
		logrus.Error(err)
		time.Sleep(start)
		start *= 2
	}
	ns, err := l.NsClient.Get(obj.Spec.TargetNamespace, metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return obj, err
	} else if errors.IsNotFound(err) {
		return obj, nil
	}
	appIds := strings.Split(ns.Annotations[AppIDsLabel], ",")
	appAnno := ""
	for _, appID := range appIds {
		if appID == obj.Name {
			continue
		}
		appAnno += appID + ","
	}
	if appAnno == "" {
		delete(ns.Annotations, AppIDsLabel)
	} else {
		appAnno = strings.TrimSuffix(appAnno, ",")
		ns.Annotations[AppIDsLabel] = appAnno
	}
	if _, err := l.NsClient.Update(ns); err != nil {
		return obj, err
	}
	return obj, nil
}

func (l *Lifecycle) Run(obj *v3.App, template, templateDir, notes string) error {
	tempDir, err := ioutil.TempDir("", "kubeconfig-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)
	kubeConfigPath, err := l.writeKubeConfig(obj, tempDir)
	if err != nil {
		return err
	}
	if err := helmInstall(templateDir, kubeConfigPath, obj); err != nil {
		return err
	}
	return l.createAppRevision(obj, template, notes, false)
}

func (l *Lifecycle) createAppRevision(obj *v3.App, template, notes string, failed bool) error {
	_, projectName := ref.Parse(obj.Spec.ProjectName)
	appRevisionClient := l.AppRevisionGetter.AppRevisions(projectName)
	release := &v3.AppRevision{}
	release.GenerateName = "apprevision-"
	release.Labels = map[string]string{
		appLabel: obj.Name,
	}
	if failed {
		release.Labels[failedLabel] = "true"
	}
	release.Status.Answers = obj.Spec.Answers
	release.Status.ProjectName = projectName
	release.Status.ExternalID = obj.Spec.ExternalID
	digest := sha256.New()
	digest.Write([]byte(template))
	tag := hex.EncodeToString(digest.Sum(nil))
	if _, err := l.TemplateContentClient.Get(tag, metav1.GetOptions{}); errors.IsNotFound(err) {
		tc := &mgmtv3.TemplateContent{}
		tc.Name = tag
		tc.Data = template
		if _, err := l.TemplateContentClient.Create(tc); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}
	release.Status.Digest = tag
	createdRevision, err := appRevisionClient.Create(release)
	if err != nil {
		return err
	}
	obj.Spec.AppRevisionName = createdRevision.Name
	obj.Status.Notes = notes
	if obj.Spec.Files != nil {
		obj.Status.AppliedFiles = obj.Spec.Files
	}
	return err
}

func (l *Lifecycle) writeKubeConfig(obj *v3.App, tempDir string) (string, error) {
	userID := obj.Annotations["field.cattle.io/creatorId"]
	user, err := l.UserClient.Get(userID, metav1.GetOptions{})
	if err != nil {
		return "", err
	}
	token, err := l.UserManager.EnsureToken(helmTokenPrefix+user.Name, description, user.Name)
	if err != nil {
		return "", err
	}

	kubeConfig := l.KubeConfigGetter.KubeConfig(l.ClusterName, token)
	for k := range kubeConfig.Clusters {
		kubeConfig.Clusters[k].InsecureSkipTLSVerify = true
	}
	if err := os.MkdirAll(filepath.Join(tempDir, obj.Namespace), 0755); err != nil {
		return "", err
	}
	kubeConfigPath := filepath.Join(tempDir, obj.Namespace, ".kubeconfig")
	if err := clientcmd.WriteToFile(*kubeConfig, kubeConfigPath); err != nil {
		return "", err
	}
	return kubeConfigPath, nil
}
<commit_msg>App cannot be removed if the creator is deleted<commit_after>package helm

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"time"

	"github.com/rancher/rancher/pkg/controllers/management/compose/common"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/rancher/pkg/systemaccount"
	corev1 "github.com/rancher/types/apis/core/v1"
	mgmtv3 "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/rancher/types/user"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	helmTokenPrefix = "helm-token-"
	description     = "token for helm chart deployment"
	AppIDsLabel     = "cattle.io/appIds"
)

func Register(ctx context.Context, user *config.UserContext, kubeConfigGetter common.KubeConfigGetter) {
	appClient := user.Management.Project.Apps("")
	stackLifecycle := &Lifecycle{
		KubeConfigGetter:      kubeConfigGetter,
		SystemAccountManager:  systemaccount.NewManager(user.Management),
		TokenClient:           user.Management.Management.Tokens(""),
		UserClient:            user.Management.Management.Users(""),
		UserManager:           user.Management.UserManager,
		K8sClient:             user.K8sClient,
		TemplateVersionClient: user.Management.Management.TemplateVersions(""),
		ListenConfigClient:    user.Management.Management.ListenConfigs(""),
		ClusterName:           user.ClusterName,
		TemplateContentClient: user.Management.Management.TemplateContents(""),
		AppRevisionGetter:     user.Management.Project,
		AppGetter:             user.Management.Project,
		NsClient:              user.Core.Namespaces(""),
	}
	appClient.AddClusterScopedLifecycle(ctx, "helm-controller", user.ClusterName, stackLifecycle)
}

type Lifecycle struct {
	KubeConfigGetter      common.KubeConfigGetter
	SystemAccountManager  *systemaccount.Manager
	UserManager           user.Manager
	TokenClient           mgmtv3.TokenInterface
	UserClient            mgmtv3.UserInterface
	TemplateVersionClient mgmtv3.TemplateVersionInterface
	K8sClient             kubernetes.Interface
	ListenConfigClient    mgmtv3.ListenConfigInterface
	ClusterName           string
	TemplateContentClient mgmtv3.TemplateContentInterface
	AppRevisionGetter     v3.AppRevisionsGetter
	AppGetter             v3.AppsGetter
	NsClient              corev1.NamespaceInterface
}

func (l *Lifecycle) Create(obj *v3.App) (runtime.Object, error) {
	v3.AppConditionMigrated.True(obj)
	return obj, nil
}

func (l *Lifecycle) Updated(obj *v3.App) (runtime.Object, error) {
	if obj.Spec.ExternalID == "" && len(obj.Spec.Files) == 0 {
		return obj, nil
	}
	// always refresh app to avoid updating app twice
	_, projectName := ref.Parse(obj.Spec.ProjectName)
	var err error
	obj, err = l.AppGetter.Apps(projectName).Get(obj.Name, metav1.GetOptions{})
	if err != nil {
		return obj, err
	}
	// if app was created before 2.1, run migrate to install a no-op helm release
	newObj, err := v3.AppConditionMigrated.Once(obj, func() (runtime.Object, error) {
		return l.DeployApp(obj)
	})
	if err != nil {
		return obj, err
	}
	obj = newObj.(*v3.App)
	appRevisionClient := l.AppRevisionGetter.AppRevisions(projectName)
	if obj.Spec.AppRevisionName != "" {
		currentRevision, err := appRevisionClient.Get(obj.Spec.AppRevisionName, metav1.GetOptions{})
		if err != nil {
			return nil, err
		}
		if obj.Spec.ExternalID != "" {
			if currentRevision.Status.ExternalID == obj.Spec.ExternalID && reflect.DeepEqual(currentRevision.Status.Answers, obj.Spec.Answers) {
				return obj, nil
			}
		}
		if obj.Status.AppliedFiles != nil {
			if reflect.DeepEqual(obj.Status.AppliedFiles, obj.Spec.Files) && reflect.DeepEqual(currentRevision.Status.Answers, obj.Spec.Answers) {
				return obj, nil
			}
		}
	}
	result, err := l.DeployApp(obj)
	if err != nil {
		return result, err
	}
	ns, err := l.NsClient.Get(obj.Spec.TargetNamespace, metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return result, err
	} else if errors.IsNotFound(err) {
		return result, nil
	}
	if ns.Annotations[AppIDsLabel] == "" {
		ns.Annotations[AppIDsLabel] = obj.Name
	} else {
		parts := strings.Split(ns.Annotations[AppIDsLabel], ",")
		appIDs := map[string]struct{}{}
		for _, part := range parts {
			appIDs[part] = struct{}{}
		}
		appIDs[obj.Name] = struct{}{}
		appIDList := []string{}
		for k := range appIDs {
			appIDList = append(appIDList, k)
		}
		sort.Strings(appIDList)
		ns.Annotations[AppIDsLabel] = strings.Join(appIDList, ",")
	}
	if _, err := l.NsClient.Update(ns); err != nil {
		return result, err
	}
	return result, nil
}

func (l *Lifecycle) DeployApp(obj *v3.App) (*v3.App, error) {
	newObj, err := v3.AppConditionInstalled.Do(obj, func() (runtime.Object, error) {
		template, notes, tempDir, err := generateTemplates(obj, l.TemplateVersionClient, l.TemplateContentClient)
		defer os.RemoveAll(tempDir)
		if err != nil {
			return obj, err
		}
		if err := l.Run(obj, template, tempDir, notes); err != nil {
			return obj, err
		}
		return obj, nil
	})
	return newObj.(*v3.App), err
}

func (l *Lifecycle) Remove(obj *v3.App) (runtime.Object, error) {
	_, projectName := ref.Parse(obj.Spec.ProjectName)
	appRevisionClient := l.AppRevisionGetter.AppRevisions(projectName)
	revisions, err := appRevisionClient.List(metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", appLabel, obj.Name),
	})
	if err != nil {
		return obj, err
	}
	for _, revision := range revisions.Items {
		if err := appRevisionClient.Delete(revision.Name, &metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
			return obj, err
		}
	}
	tempDir, err := ioutil.TempDir("", "helm-")
	if err != nil {
		return obj, err
	}
	defer os.RemoveAll(tempDir)
	kubeConfigPath, err := l.writeKubeConfig(obj, tempDir, true)
	if err != nil {
		return obj, err
	}
	// try three times and succeed
	start := time.Second * 1
	for i := 0; i < 3; i++ {
		if err := helmDelete(kubeConfigPath, obj); err == nil {
			break
		}
		logrus.Error(err)
		time.Sleep(start)
		start *= 2
	}
	ns, err := l.NsClient.Get(obj.Spec.TargetNamespace, metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return obj, err
	} else if errors.IsNotFound(err) {
		return obj, nil
	}
	appIds := strings.Split(ns.Annotations[AppIDsLabel], ",")
	appAnno := ""
	for _, appID := range appIds {
		if appID == obj.Name {
			continue
		}
		appAnno += appID + ","
	}
	if appAnno == "" {
		delete(ns.Annotations, AppIDsLabel)
	} else {
		appAnno = strings.TrimSuffix(appAnno, ",")
		ns.Annotations[AppIDsLabel] = appAnno
	}
	if _, err := l.NsClient.Update(ns); err != nil {
		return obj, err
	}
	return obj, nil
}

func (l *Lifecycle) Run(obj *v3.App, template, templateDir, notes string) error {
	tempDir, err := ioutil.TempDir("", "kubeconfig-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)
	kubeConfigPath, err := l.writeKubeConfig(obj, tempDir, false)
	if err != nil {
		return err
	}
	if err := helmInstall(templateDir, kubeConfigPath, obj); err != nil {
		return err
	}
	return l.createAppRevision(obj, template, notes, false)
}

func (l *Lifecycle) createAppRevision(obj *v3.App, template, notes string, failed bool) error {
	_, projectName := ref.Parse(obj.Spec.ProjectName)
	appRevisionClient := l.AppRevisionGetter.AppRevisions(projectName)
	release := &v3.AppRevision{}
	release.GenerateName = "apprevision-"
	release.Labels = map[string]string{
		appLabel: obj.Name,
	}
	if failed {
		release.Labels[failedLabel] = "true"
	}
	release.Status.Answers = obj.Spec.Answers
	release.Status.ProjectName = projectName
	release.Status.ExternalID = obj.Spec.ExternalID
	digest := sha256.New()
	digest.Write([]byte(template))
	tag := hex.EncodeToString(digest.Sum(nil))
	if _, err := l.TemplateContentClient.Get(tag, metav1.GetOptions{}); errors.IsNotFound(err) {
		tc := &mgmtv3.TemplateContent{}
		tc.Name = tag
		tc.Data = template
		if _, err := l.TemplateContentClient.Create(tc); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}
	release.Status.Digest = tag
	createdRevision, err := appRevisionClient.Create(release)
	if err != nil {
		return err
	}
	obj.Spec.AppRevisionName = createdRevision.Name
	obj.Status.Notes = notes
	if obj.Spec.Files != nil {
		obj.Status.AppliedFiles = obj.Spec.Files
	}
	return err
}

func (l *Lifecycle) writeKubeConfig(obj *v3.App, tempDir string, remove bool) (string, error) {
	var token string

	userID := obj.Annotations["field.cattle.io/creatorId"]
	user, err := l.UserClient.Get(userID, metav1.GetOptions{})
	if err != nil && !errors.IsNotFound(err) {
		return "", err
	} else if errors.IsNotFound(err) && remove {
		token, err = l.SystemAccountManager.GetOrCreateProjectSystemToken(obj.Namespace)
	} else if err == nil {
		token, err = l.UserManager.EnsureToken(helmTokenPrefix+user.Name, description, user.Name)
	}
	if err != nil {
		return "", err
	}

	kubeConfig := l.KubeConfigGetter.KubeConfig(l.ClusterName, token)
	for k := range kubeConfig.Clusters {
		kubeConfig.Clusters[k].InsecureSkipTLSVerify = true
	}
	if err := os.MkdirAll(filepath.Join(tempDir, obj.Namespace), 0755); err != nil {
		return "", err
	}
	kubeConfigPath := filepath.Join(tempDir, obj.Namespace, ".kubeconfig")
	if err := clientcmd.WriteToFile(*kubeConfig, kubeConfigPath); err != nil {
		return "", err
	}
	return kubeConfigPath, nil
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"net/http"

	"net/http/httptest"
	"testing"
)

var fakeHarborWebhook = ` {
    "type": "pushImage",
    "occur_at": 1582640688,
    "operator": "user",
    "event_data": {
        "resources": [
            {
                "digest": "sha256:b4758aaed11c155a476b9857e1178f157759c99cb04c907a04993f5481eff848",
                "tag": "1.2.3",
                "resource_url": "quay.io/mynamespace/repository:1.2.3"
            }
        ],
        "repository": {
            "date_created": 1582634337,
            "name": "repository",
            "namespace": "mynamespace",
            "repo_full_name": "mynamespace/repository",
            "repo_type": "private"
        }
    }
}
`

func TestHarborWebhookHandler(t *testing.T) {

	fp := &fakeProvider{}
	srv, teardown := NewTestingServer(fp)
	defer teardown()

	req, err := http.NewRequest("POST", "/v1/webhooks/harbor", bytes.NewBuffer([]byte(fakeHarborWebhook)))
	if err != nil {
		t.Fatalf("failed to create req: %s", err)
	}

	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	srv.router.ServeHTTP(rec, req)
	if rec.Code != 200 {
		t.Errorf("unexpected status code: %d", rec.Code)

		t.Log(rec.Body.String())
	}

	if len(fp.submitted) != 1 {
		t.Fatalf("unexpected number of events submitted: %d", len(fp.submitted))
	}

	if fp.submitted[0].Repository.Name != "quay.io/mynamespace/repository" {
		t.Errorf("expected quay.io/mynamespace/repository but got %s", fp.submitted[0].Repository.Name)
	}

	if fp.submitted[0].Repository.Tag != "1.2.3" {
		t.Errorf("expected 1.2.3 but got %s", fp.submitted[0].Repository.Tag)
	}
}

var fakeHarborWebhookMalformed = ` {
	"type": "pushImage",
	"occur_at": 1582640688,
	"operator": "user",
	"event_data": {
			"resources": [
					{
							"digest": "sha256:b4758aaed11c155a476b9857e1178f157759c99cb04c907a04993f5481eff848",
							"tag": "1.2.3",
							"resource_url": "mynamespace/repository"
					}
			],
			"repository": {
					"date_created": 1582634337,
					"name": "repository",
					"namespace": "mynamespace",
					"repo_full_name": "mynamespace/repository",
					"repo_type": "private"
			}
	}
}
`

func TestHarborWebhookHandlerMalformed(t *testing.T) {

	fp := &fakeProvider{}
	srv, teardown := NewTestingServer(fp)
	defer teardown()

	req, err := http.NewRequest("POST", "/v1/webhooks/harbor", bytes.NewBuffer([]byte(fakeHarborWebhookMalformed)))
	if err != nil {
		t.Fatalf("failed to create req: %s", err)
	}

	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	srv.router.ServeHTTP(rec, req)
	if rec.Code != 200 {
		t.Errorf("unexpected status code: %d", rec.Code)

		t.Log(rec.Body.String())
	}

	if len(fp.submitted) != 1 {
		t.Fatalf("unexpected number of events submitted: %d", len(fp.submitted))
	}

	if fp.submitted[0].Repository.Name != "index.docker.io/mynamespace/repository" {
		t.Errorf("expected index.docker.io/mynamespace/repository but got %s", fp.submitted[0].Repository.Name)
	}

	if fp.submitted[0].Repository.Tag != "latest" {
		t.Errorf("expected latest but got %s", fp.submitted[0].Repository.Tag)
	}
}
<commit_msg>Added new test for harbor for new webhook type<commit_after>package http

import (
	"bytes"
	"net/http"

	"net/http/httptest"
	"testing"
)

var fakeHarborWebhook = ` {
    "type": "pushImage",
    "occur_at": 1582640688,
    "operator": "user",
    "event_data": {
        "resources": [
            {
                "digest": "sha256:b4758aaed11c155a476b9857e1178f157759c99cb04c907a04993f5481eff848",
                "tag": "1.2.3",
                "resource_url": "quay.io/mynamespace/repository:1.2.3"
            }
        ],
        "repository": {
            "date_created": 1582634337,
            "name": "repository",
            "namespace": "mynamespace",
            "repo_full_name": "mynamespace/repository",
            "repo_type": "private"
        }
    }
}
`

var fakeHarborWebhook2 = `
	{
		"type": "PUSH_ARTIFACT",
		"occur_at": 1582640688,
		"operator": "user",
		"event_data": {
		  "resources": [
			{
			  "digest": "sha256:b4758aaed11c155a476b9857e1178f157759c99cb04c907a04993f5481eff848",
			  "tag": "latest",
			  "resource_url": "quay.io/mynamespace/repository:1.2.3"
			}
		  ],
		  "repository": {
			"date_created": 1582634337,
			"name": "repository",
			"namespace": "mynamespace",
			"repo_full_name": "mynamespace/repository",
			"repo_type": "private"
		  }
		}
	}
`

func TestHarborWebhookHandler(t *testing.T) {

	fp := &fakeProvider{}
	srv, teardown := NewTestingServer(fp)
	defer teardown()

	req, err := http.NewRequest("POST", "/v1/webhooks/harbor", bytes.NewBuffer([]byte(fakeHarborWebhook)))
	if err != nil {
		t.Fatalf("failed to create req: %s", err)
	}

	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	srv.router.ServeHTTP(rec, req)
	if rec.Code != 200 {
		t.Errorf("unexpected status code: %d", rec.Code)

		t.Log(rec.Body.String())
	}

	if len(fp.submitted) != 1 {
		t.Fatalf("unexpected number of events submitted: %d", len(fp.submitted))
	}

	if fp.submitted[0].Repository.Name != "quay.io/mynamespace/repository" {
		t.Errorf("expected quay.io/mynamespace/repository but got %s", fp.submitted[0].Repository.Name)
	}

	if fp.submitted[0].Repository.Tag != "1.2.3" {
		t.Errorf("expected 1.2.3 but got %s", fp.submitted[0].Repository.Tag)
	}
}

func TestHarborWebhookHandler2(t *testing.T) {

	fp := &fakeProvider{}
	srv, teardown := NewTestingServer(fp)
	defer teardown()

	req, err := http.NewRequest("POST", "/v1/webhooks/harbor", bytes.NewBuffer([]byte(fakeHarborWebhook2)))
	if err != nil {
		t.Fatalf("failed to create req: %s", err)
	}

	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	srv.router.ServeHTTP(rec, req)
	if rec.Code != 200 {
		t.Errorf("unexpected status code: %d", rec.Code)

		t.Log(rec.Body.String())
	}

	if len(fp.submitted) != 1 {
		t.Fatalf("unexpected number of events submitted: %d", len(fp.submitted))
	}

	if fp.submitted[0].Repository.Name != "quay.io/mynamespace/repository" {
		t.Errorf("expected quay.io/mynamespace/repository but got %s", fp.submitted[0].Repository.Name)
	}

	if fp.submitted[0].Repository.Tag != "1.2.3" {
		t.Errorf("expected 1.2.3 but got %s", fp.submitted[0].Repository.Tag)
	}
}

var fakeHarborWebhookMalformed = ` {
	"type": "pushImage",
	"occur_at": 1582640688,
	"operator": "user",
	"event_data": {
			"resources": [
					{
							"digest": "sha256:b4758aaed11c155a476b9857e1178f157759c99cb04c907a04993f5481eff848",
							"tag": "1.2.3",
							"resource_url": "mynamespace/repository"
					}
			],
			"repository": {
					"date_created": 1582634337,
					"name": "repository",
					"namespace": "mynamespace",
					"repo_full_name": "mynamespace/repository",
					"repo_type": "private"
			}
	}
}
`

func TestHarborWebhookHandlerMalformed(t *testing.T) {

	fp := &fakeProvider{}
	srv, teardown := NewTestingServer(fp)
	defer teardown()

	req, err := http.NewRequest("POST", "/v1/webhooks/harbor", bytes.NewBuffer([]byte(fakeHarborWebhookMalformed)))
	if err != nil {
		t.Fatalf("failed to create req: %s", err)
	}

	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	srv.router.ServeHTTP(rec, req)
	if rec.Code != 200 {
		t.Errorf("unexpected status code: %d", rec.Code)

		t.Log(rec.Body.String())
	}

	if len(fp.submitted) != 1 {
		t.Fatalf("unexpected number of events submitted: %d", len(fp.submitted))
	}

	if fp.submitted[0].Repository.Name != "index.docker.io/mynamespace/repository" {
		t.Errorf("expected index.docker.io/mynamespace/repository but got %s", fp.submitted[0].Repository.Name)
	}

	if fp.submitted[0].Repository.Tag != "latest" {
		t.Errorf("expected latest but got %s", fp.submitted[0].Repository.Tag)
	}
}
<|endoftext|>


<commit_before>// +build linux

/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package kvm2

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

	"github.com/docker/machine/libmachine/drivers"
	"k8s.io/minikube/pkg/drivers/kvm"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/registry"
)

const (
	docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/"
)

func init() {
	if err := registry.Register(registry.DriverDef{
		Name:     driver.KVM2,
		Config:   configure,
		Status:   status,
		Priority: registry.Preferred,
	}); err != nil {
		panic(fmt.Sprintf("register failed: %v", err))
	}
}

func configure(mc config.MachineConfig) interface{} {
	name := config.GetMachineName()
	return kvm.Driver{
		BaseDriver: &drivers.BaseDriver{
			MachineName: name,
			StorePath:   localpath.MiniPath(),
			SSHUser:     "docker",
		},
		Memory:         mc.Memory,
		CPU:            mc.CPUs,
		Network:        mc.KVMNetwork,
		PrivateNetwork: "minikube-net",
		Boot2DockerURL: mc.Downloader.GetISOFileURI(mc.MinikubeISO),
		DiskSize:       mc.DiskSize,
		DiskPath:       filepath.Join(localpath.MiniPath(), "machines", name, fmt.Sprintf("%s.rawdisk", name)),
		ISO:            filepath.Join(localpath.MiniPath(), "machines", name, "boot2docker.iso"),
		GPU:            mc.KVMGPU,
		Hidden:         mc.KVMHidden,
		ConnectionURI:  mc.KVMQemuURI,
	}
}

func status() registry.State {
	path, err := exec.LookPath("virt-host-validate")
	if err != nil {
		return registry.State{Error: err, Fix: "Install libvirt", Doc: docURL}
	}

	err = exec.Command(path, "qemu").Run()
	if err != nil {
		return registry.State{Installed: true, Error: err, Fix: "Check output of 'virt-host-validate qemu'", Doc: docURL}
	}

	err = exec.Command("virsh", "list").Run()
	if err != nil {
		return registry.State{Installed: true, Error: err, Fix: "Check output of 'virsh list'", Doc: docURL}
	}

	return registry.State{Installed: true, Healthy: true}
}
<commit_msg>Remove dependency between minikube and kvm driver<commit_after>// +build linux

/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package kvm2

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

	"github.com/docker/machine/libmachine/drivers"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/registry"
)

const (
	docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/"
)

func init() {
	if err := registry.Register(registry.DriverDef{
		Name:     driver.KVM2,
		Config:   configure,
		Status:   status,
		Priority: registry.Preferred,
	}); err != nil {
		panic(fmt.Sprintf("register failed: %v", err))
	}
}

// This is duplicate of kvm.Driver. Avoids importing the kvm2 driver, which requires cgo & libvirt.
type kvmDriver struct {
	*drivers.BaseDriver

	Memory         int
	DiskSize       int
	CPU            int
	Network        string
	PrivateNetwork string
	ISO            string
	Boot2DockerURL string
	DiskPath       string
	GPU            bool
	Hidden         bool
	ConnectionURI  string
}

func configure(mc config.MachineConfig) interface{} {
	name := config.GetMachineName()
	return kvmDriver{
		BaseDriver: &drivers.BaseDriver{
			MachineName: name,
			StorePath:   localpath.MiniPath(),
			SSHUser:     "docker",
		},
		Memory:         mc.Memory,
		CPU:            mc.CPUs,
		Network:        mc.KVMNetwork,
		PrivateNetwork: "minikube-net",
		Boot2DockerURL: mc.Downloader.GetISOFileURI(mc.MinikubeISO),
		DiskSize:       mc.DiskSize,
		DiskPath:       filepath.Join(localpath.MiniPath(), "machines", name, fmt.Sprintf("%s.rawdisk", name)),
		ISO:            filepath.Join(localpath.MiniPath(), "machines", name, "boot2docker.iso"),
		GPU:            mc.KVMGPU,
		Hidden:         mc.KVMHidden,
		ConnectionURI:  mc.KVMQemuURI,
	}
}

func status() registry.State {
	path, err := exec.LookPath("virt-host-validate")
	if err != nil {
		return registry.State{Error: err, Fix: "Install libvirt", Doc: docURL}
	}

	err = exec.Command(path, "qemu").Run()
	if err != nil {
		return registry.State{Installed: true, Error: err, Fix: "Check output of 'virt-host-validate qemu'", Doc: docURL}
	}

	err = exec.Command("virsh", "list").Run()
	if err != nil {
		return registry.State{Installed: true, Error: err, Fix: "Check output of 'virsh list'", Doc: docURL}
	}

	return registry.State{Installed: true, Healthy: true}
}
<|endoftext|>


<commit_before>package controller_manager

import (
	"context"
	"strings"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	exutil "github.com/openshift/origin/test/extended/util"
)

func waitForServiceAccountToken(client kubernetes.Interface, ns, name string, attempts int, interval time.Duration) (string, error) {
	for i := 0; i <= attempts; i++ {
		time.Sleep(interval)
		token, err := getServiceAccountToken(client, ns, name)
		if err != nil {
			return "", err
		}
		if len(token) > 0 {
			return token, nil
		}
	}
	return "", nil
}

func getServiceAccountToken(client kubernetes.Interface, ns, name string) (string, error) {
	secrets, err := client.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{})
	if err != nil {
		return "", err
	}
	for _, secret := range secrets.Items {
		if secret.Type == corev1.SecretTypeServiceAccountToken && secret.Annotations[corev1.ServiceAccountNameKey] == name {
			sa, err := client.CoreV1().ServiceAccounts(ns).Get(context.Background(), name, metav1.GetOptions{})
			if err != nil {
				return "", err
			}

			for _, ref := range sa.Secrets {
				if ref.Name == secret.Name {
					return string(secret.Data[corev1.ServiceAccountTokenKey]), nil
				}
			}

		}
	}

	return "", nil
}

var _ = g.Describe("[sig-devex][Feature:OpenShiftControllerManager]", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("pull-secrets")

	g.It("TestAutomaticCreationOfPullSecrets", func() {
		t := g.GinkgoT()

		clusterAdminKubeClient := oc.AdminKubeClient()
		saNamespace := oc.Namespace()
		saName := "default"

		// Get a service account token
		saToken, err := waitForServiceAccountToken(clusterAdminKubeClient, saNamespace, saName, 20, time.Second)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		if len(saToken) == 0 {
			t.Errorf("token was not created")
		}

		// Get the matching dockercfg secret
		_, saPullSecret, err := waitForServiceAccountPullSecret(clusterAdminKubeClient, saNamespace, saName, 20, time.Second)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		if len(saPullSecret) == 0 {
			t.Errorf("pull secret was not created")
		}

		imageConfig, err := oc.AdminConfigClient().ConfigV1().Images().Get(context.Background(), "cluster", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		if !strings.Contains(saPullSecret, imageConfig.Status.InternalRegistryHostname) {
			t.Errorf("missing %q in %v", imageConfig.Status.InternalRegistryHostname, saPullSecret)
		}

		if len(imageConfig.Spec.ExternalRegistryHostnames) > 0 {
			if !strings.Contains(saPullSecret, imageConfig.Spec.ExternalRegistryHostnames[0]) {
				t.Errorf("missing %q in %v", imageConfig.Spec.ExternalRegistryHostnames[0], saPullSecret)
			}
		}

	})
})

func waitForServiceAccountPullSecret(client kubernetes.Interface, ns, name string, attempts int, interval time.Duration) (string, string, error) {
	for i := 0; i <= attempts; i++ {
		time.Sleep(interval)
		secretName, dockerCfg, err := getServiceAccountPullSecret(client, ns, name)
		if err != nil {
			return "", "", err
		}
		if len(dockerCfg) > 2 {
			return secretName, dockerCfg, nil
		}
	}
	return "", "", nil
}

func getServiceAccountPullSecret(client kubernetes.Interface, ns, name string) (string, string, error) {
	secrets, err := client.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{})
	if err != nil {
		return "", "", err
	}
	for _, secret := range secrets.Items {
		if secret.Type == corev1.SecretTypeDockercfg && secret.Annotations[corev1.ServiceAccountNameKey] == name {
			return secret.Name, string(secret.Data[corev1.DockerConfigKey]), nil
		}
	}
	return "", "", nil
}

var _ = g.Describe("[sig-devex][Feature:OpenShiftControllerManager]", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("pull-secrets")

	g.AfterEach(func() {
		if g.CurrentGinkgoTestDescription().Failed {
			g.By("dumping openshift-controller-manager logs")
			exutil.DumpPodLogsStartingWithInNamespace("controller-manager", "openshift-controller-manager", oc.AsAdmin())
		}
	})

	g.It("TestDockercfgTokenDeletedController", func() {
		t := g.GinkgoT()

		clusterAdminKubeClient := oc.AdminKubeClient()
		saNamespace := oc.Namespace()

		g.By("creating test service account")
		sa := &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{Name: "sa1", Namespace: saNamespace},
		}

		sa, err := clusterAdminKubeClient.CoreV1().ServiceAccounts(sa.Namespace).Create(context.Background(), sa, metav1.CreateOptions{})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		g.By("waiting for service account's pull secret to be created")
		dockercfgSecretName, _, err := waitForServiceAccountPullSecret(clusterAdminKubeClient, sa.Namespace, sa.Name, 20, time.Second)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(dockercfgSecretName) == 0 {
			t.Fatal("pull secret was not created")
		}

		// Get the matching secret's name
		dockercfgSecret, err := clusterAdminKubeClient.CoreV1().Secrets(sa.Namespace).Get(context.Background(), dockercfgSecretName, metav1.GetOptions{})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		secretName := dockercfgSecret.Annotations["openshift.io/token-secret.name"]
		if len(secretName) == 0 {
			t.Fatal("secret was not created")
		}

		g.By("deleting the token used to generate the pull secret")
		if err := clusterAdminKubeClient.CoreV1().Secrets(sa.Namespace).Delete(context.Background(), secretName, metav1.DeleteOptions{}); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		// Expect the matching dockercfg secret to also be deleted
		if err := wait.Poll(10*time.Second, 10*time.Minute, func() (bool, error) {
			_, err := clusterAdminKubeClient.CoreV1().Secrets(sa.Namespace).Get(
				context.Background(),
				dockercfgSecretName,
				metav1.GetOptions{},
			)
			if err == nil {
				e2e.Logf("secret %s/%s exists", sa.Namespace, dockercfgSecretName)
				return false, nil
			}
			if errors.IsNotFound(err) {
				return true, nil
			}
			return false, err
		}); err != nil {
			t.Fatalf("error waiting for pull secret deletion: %v", err)
		}
	})
})
<commit_msg>Bug 2081012: Don't wait for sa token<commit_after>package controller_manager

import (
	"context"
	"strings"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	exutil "github.com/openshift/origin/test/extended/util"
)

func waitForServiceAccountToken(client kubernetes.Interface, ns, name string, attempts int, interval time.Duration) (string, error) {
	for i := 0; i <= attempts; i++ {
		time.Sleep(interval)
		token, err := getServiceAccountToken(client, ns, name)
		if err != nil {
			return "", err
		}
		if len(token) > 0 {
			return token, nil
		}
	}
	return "", nil
}

func getServiceAccountToken(client kubernetes.Interface, ns, name string) (string, error) {
	secrets, err := client.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{})
	if err != nil {
		return "", err
	}
	for _, secret := range secrets.Items {
		if secret.Type == corev1.SecretTypeServiceAccountToken && secret.Annotations[corev1.ServiceAccountNameKey] == name {
			sa, err := client.CoreV1().ServiceAccounts(ns).Get(context.Background(), name, metav1.GetOptions{})
			if err != nil {
				return "", err
			}

			for _, ref := range sa.Secrets {
				if ref.Name == secret.Name {
					return string(secret.Data[corev1.ServiceAccountTokenKey]), nil
				}
			}

		}
	}

	return "", nil
}

var _ = g.Describe("[sig-devex][Feature:OpenShiftControllerManager]", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("pull-secrets")

	g.It("TestAutomaticCreationOfPullSecrets", func() {
		t := g.GinkgoT()

		clusterAdminKubeClient := oc.AdminKubeClient()
		saNamespace := oc.Namespace()
		saName := "default"

		// Get the matching dockercfg secret
		_, saPullSecret, err := waitForServiceAccountPullSecret(clusterAdminKubeClient, saNamespace, saName, 20, time.Second)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		if len(saPullSecret) == 0 {
			t.Errorf("pull secret was not created")
		}

		imageConfig, err := oc.AdminConfigClient().ConfigV1().Images().Get(context.Background(), "cluster", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		if !strings.Contains(saPullSecret, imageConfig.Status.InternalRegistryHostname) {
			t.Errorf("missing %q in %v", imageConfig.Status.InternalRegistryHostname, saPullSecret)
		}

		if len(imageConfig.Spec.ExternalRegistryHostnames) > 0 {
			if !strings.Contains(saPullSecret, imageConfig.Spec.ExternalRegistryHostnames[0]) {
				t.Errorf("missing %q in %v", imageConfig.Spec.ExternalRegistryHostnames[0], saPullSecret)
			}
		}

	})
})

func waitForServiceAccountPullSecret(client kubernetes.Interface, ns, name string, attempts int, interval time.Duration) (string, string, error) {
	for i := 0; i <= attempts; i++ {
		time.Sleep(interval)
		secretName, dockerCfg, err := getServiceAccountPullSecret(client, ns, name)
		if err != nil {
			return "", "", err
		}
		if len(dockerCfg) > 2 {
			return secretName, dockerCfg, nil
		}
	}
	return "", "", nil
}

func getServiceAccountPullSecret(client kubernetes.Interface, ns, name string) (string, string, error) {
	secrets, err := client.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{})
	if err != nil {
		return "", "", err
	}
	for _, secret := range secrets.Items {
		if secret.Type == corev1.SecretTypeDockercfg && secret.Annotations[corev1.ServiceAccountNameKey] == name {
			return secret.Name, string(secret.Data[corev1.DockerConfigKey]), nil
		}
	}
	return "", "", nil
}

var _ = g.Describe("[sig-devex][Feature:OpenShiftControllerManager]", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("pull-secrets")

	g.AfterEach(func() {
		if g.CurrentGinkgoTestDescription().Failed {
			g.By("dumping openshift-controller-manager logs")
			exutil.DumpPodLogsStartingWithInNamespace("controller-manager", "openshift-controller-manager", oc.AsAdmin())
		}
	})

	g.It("TestDockercfgTokenDeletedController", func() {
		t := g.GinkgoT()

		clusterAdminKubeClient := oc.AdminKubeClient()
		saNamespace := oc.Namespace()

		g.By("creating test service account")
		sa := &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{Name: "sa1", Namespace: saNamespace},
		}

		sa, err := clusterAdminKubeClient.CoreV1().ServiceAccounts(sa.Namespace).Create(context.Background(), sa, metav1.CreateOptions{})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		g.By("waiting for service account's pull secret to be created")
		dockercfgSecretName, _, err := waitForServiceAccountPullSecret(clusterAdminKubeClient, sa.Namespace, sa.Name, 20, time.Second)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(dockercfgSecretName) == 0 {
			t.Fatal("pull secret was not created")
		}

		// Get the matching secret's name
		dockercfgSecret, err := clusterAdminKubeClient.CoreV1().Secrets(sa.Namespace).Get(context.Background(), dockercfgSecretName, metav1.GetOptions{})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		secretName := dockercfgSecret.Annotations["openshift.io/token-secret.name"]
		if len(secretName) == 0 {
			t.Fatal("secret was not created")
		}

		g.By("deleting the token used to generate the pull secret")
		if err := clusterAdminKubeClient.CoreV1().Secrets(sa.Namespace).Delete(context.Background(), secretName, metav1.DeleteOptions{}); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		// Expect the matching dockercfg secret to also be deleted
		if err := wait.Poll(10*time.Second, 10*time.Minute, func() (bool, error) {
			_, err := clusterAdminKubeClient.CoreV1().Secrets(sa.Namespace).Get(
				context.Background(),
				dockercfgSecretName,
				metav1.GetOptions{},
			)
			if err == nil {
				e2e.Logf("secret %s/%s exists", sa.Namespace, dockercfgSecretName)
				return false, nil
			}
			if errors.IsNotFound(err) {
				return true, nil
			}
			return false, err
		}); err != nil {
			t.Fatalf("error waiting for pull secret deletion: %v", err)
		}
	})
})
<|endoftext|>