<commit_before>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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 linuxcalls

import (
	"github.com/go-errors/errors"
	"github.com/ligato/vpp-agent/plugins/linux/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/linux/nsplugin"
	"github.com/vishvananda/netlink"
)

// LinuxInterfaceDetails is the wrapper structure for the linux interface northbound API structure.
type LinuxInterfaceDetails struct {
	Interface *interfaces.LinuxInterfaces_Interface `json:"linux_interface"`
	Meta      *LinuxInterfaceMeta                   `json:"linux_interface_meta"`
}

// LinuxInterfaceMeta is combination of proto-modelled Interface data and linux provided metadata
type LinuxInterfaceMeta struct {
	Index       int    `json:"index"`
	Name        string `json:"name"`
	Alias       string `json:"alias"`
	OperState   string `json:"oper_state"`
	Flags       string `json:"flags"`
	MacAddr     string `json:"mac_addr"`
	Mtu         int    `json:"mtu"`
	Type        string `json:"type"`
	NetNsID     int    `json:"net_ns_id"`
	NumTxQueues int    `json:"num_tx_queues"`
	TxQueueLen  int    `json:"tx_queue_len"`
	NumRxQueues int    `json:"num_rx_queues"`
}

// DumpInterfaces is an implementation of linux interface handler
func (h *NetLinkHandler) DumpInterfaces() ([]*LinuxInterfaceDetails, error) {
	var ifs []*LinuxInterfaceDetails

	ctx := nsplugin.NewNamespaceMgmtCtx()

	// Dump all interfaces from the default namespace
	dNsLinks, err := h.GetLinkList()
	if err != nil {
		return nil, errors.Errorf("failed to dump linux interfaces from default namespace: %v", err)
	}
	for _, dNsLink := range dNsLinks {
		ifDetails := &LinuxInterfaceDetails{}
		if dNsLink == nil || dNsLink.Attrs() == nil {
			h.log.Warnf("Unable to get link data for interface %s", dNsLink)
			continue
		}
		ifName := dNsLink.Attrs().Name
		link, linkAddresses, err := h.dumpInterfaceData(ifName, &nsplugin.Namespace{}, ctx)
		if err != nil {
			// Do not return error
			h.log.Errorf("failed to get interface %s data: %v", ifName, err)
			continue
		}

		// If the interface exists in mapping, copy the modelled data
		if _, meta, found := h.ifIndexes.LookupIdx(ifName); found {
			ifDetails.Interface = meta.Data
		}

		h.transformAndStore(link, linkAddresses, ifDetails)
		ifs = append(ifs, ifDetails)
	}

	// Add all known interfaces in non-default namespace
	for _, ifName := range h.ifIndexes.GetMapping().ListNames() {
		ifDetails := &LinuxInterfaceDetails{}
		_, meta, found := h.ifIndexes.LookupIdx(ifName)
		if !found {
			h.log.Warnf("Expected interface %s not found in the mapping", ifName)
			continue
		}
		if meta == nil || meta.Data == nil {
			h.log.Warnf("Expected interface %s metadata are missing", ifName)
			continue
		}
		// Skip interfaces in default NS (already processed)
		if meta.Data.Namespace == nil {
			continue
		}

		// Copy base configuration from mapping metadata. Linux specific fields are stored in LinuxInterfaceMeta.
		ifDetails.Interface = meta.Data

		// Check the interface namespace
		link, linkAddresses, err := h.dumpInterfaceData(ifName, h.nsHandler.IfNsToGeneric(meta.Data.Namespace), ctx)
		if err != nil {
			// Do not return error, read what is possible
			h.log.Errorf("failed to get interface %s data: %v", ifName, err)
			continue
		}

		if link == nil || link.Attrs() == nil {
			h.log.Warnf("Unable to get link data for interface %s", ifName)
			continue
		}

		h.transformAndStore(link, linkAddresses, ifDetails)
		ifs = append(ifs, ifDetails)
	}

	return ifs, nil
}

// LinuxInterfaceStatistics returns linux interface name/index with statistics data
type LinuxInterfaceStatistics struct {
	Name       string
	Index      int
	Statistics *netlink.LinkStatistics
}

// DumpInterfaceStatistics is an implementation of linux interface handler
func (h *NetLinkHandler) DumpInterfaceStatistics() ([]*LinuxInterfaceStatistics, error) {
	var ifs []*LinuxInterfaceStatistics

	ctx := nsplugin.NewNamespaceMgmtCtx()

	// Iterate over all known interfaces
	for _, ifName := range h.ifIndexes.GetMapping().ListNames() {
		_, meta, found := h.ifIndexes.LookupIdx(ifName)
		if !found {
			h.log.Warnf("Expected interface %s not found in the mapping", ifName)
			continue
		}
		if meta == nil || meta.Data == nil {
			h.log.Warnf("Expected interface %s metadata are missing", ifName)
			continue
		}

		// Check the interface namespace
		link, _, err := h.dumpInterfaceData(ifName, h.nsHandler.IfNsToGeneric(meta.Data.Namespace), ctx)
		if err != nil {
			// Do not return error, read what is possible
			h.log.Errorf("failed to get interface %s data: %v", ifName, err)
			continue
		}

		if link == nil || link.Attrs() == nil {
			h.log.Warnf("Unable to get link data for interface %s", ifName)
			continue
		}

		linkAttrs := link.Attrs()

		// Fill data
		linuxStats := &LinuxInterfaceStatistics{
			Name:       linkAttrs.Name,
			Index:      linkAttrs.Index,
			Statistics: linkAttrs.Statistics,
		}

		ifs = append(ifs, linuxStats)
	}

	return ifs, nil
}

// Reads interface data and ip addresses from provided namespace
func (h *NetLinkHandler) dumpInterfaceData(ifName string, ns *nsplugin.Namespace, ctx *nsplugin.NamespaceMgmtCtx) (netlink.Link, []netlink.Addr, error) {
	revert, err := h.nsHandler.SwitchNamespace(ns, ctx)
	defer revert()

	if err != nil {
		// Do not return error here, since the interface namespace could be removed in the meantime
		h.log.Debugf("interface %s cannot be read, namespace is not accessible (err: %s)", ifName, err)
		return nil, nil, nil
	}
	link, err := h.GetLinkByName(ifName)
	if err != nil {
		return nil, nil, errors.Errorf("failed to get interface %s from namespace %s: %v", ifName, ns.Name, err)
	}
	linkAddrs, err := h.GetAddressList(ifName)
	if err != nil {
		return nil, nil, errors.Errorf("failed to get interface %s addresses: %v", ifName, err)
	}

	return link, linkAddrs, nil
}

// Transform link type interface to the interface details and store IP addresses to the proto modelled data
func (h *NetLinkHandler) transformAndStore(link netlink.Link, addr []netlink.Addr, ifDetails *LinuxInterfaceDetails) {
	// Set IP addresses to model-based data
	var ipAddrs []string
	for _, linkAddr := range addr {
		ipAddrs = append(ipAddrs, linkAddr.String())
	}
	if ifDetails.Interface == nil {
		ifDetails.Interface = &interfaces.LinuxInterfaces_Interface{
			IpAddresses: ipAddrs,
		}
	} else {
		ifDetails.Interface.IpAddresses = ipAddrs
	}

	// Metadata fields
	linkAttrs := link.Attrs()
	linuxMeta := &LinuxInterfaceMeta{
		Index:       linkAttrs.Index,
		Name:        linkAttrs.Name,
		Alias:       linkAttrs.Alias,
		OperState:   linkAttrs.OperState.String(),
		Flags:       linkAttrs.Flags.String(),
		Mtu:         linkAttrs.MTU,
		Type:        linkAttrs.EncapType,
		NetNsID:     linkAttrs.NetNsID,
		NumTxQueues: linkAttrs.NumTxQueues,
		TxQueueLen:  linkAttrs.TxQLen,
		NumRxQueues: linkAttrs.NumRxQueues,
	}
	if linkAttrs.HardwareAddr != nil {
		linuxMeta.MacAddr = linkAttrs.HardwareAddr.String()
	}
	ifDetails.Meta = linuxMeta
}
<commit_msg>linux if dump uses correct name to read links<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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 linuxcalls

import (
	"github.com/go-errors/errors"
	"github.com/ligato/vpp-agent/plugins/linux/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/linux/nsplugin"
	"github.com/vishvananda/netlink"
)

// LinuxInterfaceDetails is the wrapper structure for the linux interface northbound API structure.
type LinuxInterfaceDetails struct {
	Interface *interfaces.LinuxInterfaces_Interface `json:"linux_interface"`
	Meta      *LinuxInterfaceMeta                   `json:"linux_interface_meta"`
}

// LinuxInterfaceMeta is combination of proto-modelled Interface data and linux provided metadata
type LinuxInterfaceMeta struct {
	Index       int    `json:"index"`
	Name        string `json:"name"`
	Alias       string `json:"alias"`
	OperState   string `json:"oper_state"`
	Flags       string `json:"flags"`
	MacAddr     string `json:"mac_addr"`
	Mtu         int    `json:"mtu"`
	Type        string `json:"type"`
	NetNsID     int    `json:"net_ns_id"`
	NumTxQueues int    `json:"num_tx_queues"`
	TxQueueLen  int    `json:"tx_queue_len"`
	NumRxQueues int    `json:"num_rx_queues"`
}

// DumpInterfaces is an implementation of linux interface handler
func (h *NetLinkHandler) DumpInterfaces() ([]*LinuxInterfaceDetails, error) {
	var ifs []*LinuxInterfaceDetails

	ctx := nsplugin.NewNamespaceMgmtCtx()

	// Dump all interfaces from the default namespace
	dNsLinks, err := h.GetLinkList()
	if err != nil {
		return nil, errors.Errorf("failed to dump linux interfaces from default namespace: %v", err)
	}
	for _, dNsLink := range dNsLinks {
		ifDetails := &LinuxInterfaceDetails{}
		if dNsLink == nil || dNsLink.Attrs() == nil {
			h.log.Warnf("Unable to get link data for interface %s", dNsLink)
			continue
		}
		ifName := dNsLink.Attrs().Name
		// Name is already a host name, since it is read directly form link attributes
		link, linkAddresses, err := h.dumpInterfaceData(ifName, &nsplugin.Namespace{}, ctx)
		if err != nil {
			// Do not return error
			h.log.Errorf("failed to get interface %s data: %v", ifName, err)
			continue
		}

		// If the interface exists in mapping, copy the modelled data
		if _, meta, found := h.ifIndexes.LookupIdx(ifName); found {
			ifDetails.Interface = meta.Data
		}

		h.transformAndStore(link, linkAddresses, ifDetails)
		ifs = append(ifs, ifDetails)
	}

	// Add all known interfaces in non-default namespace
	for _, ifName := range h.ifIndexes.GetMapping().ListNames() {
		ifDetails := &LinuxInterfaceDetails{}
		_, meta, found := h.ifIndexes.LookupIdx(ifName)
		if !found {
			h.log.Warnf("Expected interface %s not found in the mapping", ifName)
			continue
		}
		if meta == nil || meta.Data == nil {
			h.log.Warnf("Expected interface %s metadata are missing", ifName)
			continue
		}
		// Skip interfaces in default NS (already processed)
		if meta.Data.Namespace == nil {
			continue
		}

		// Copy base configuration from mapping metadata. Linux specific fields are stored in LinuxInterfaceMeta.
		ifDetails.Interface = meta.Data

		// Use host name to dump interfaces via netlink
		link, linkAddresses, err := h.dumpInterfaceData(meta.Data.HostIfName, h.nsHandler.IfNsToGeneric(meta.Data.Namespace), ctx)
		if err != nil {
			// Do not return error, read what is possible
			h.log.Errorf("failed to get interface %s data: %v", ifName, err)
			continue
		}

		if link == nil || link.Attrs() == nil {
			h.log.Warnf("Unable to get link data for interface %s", ifName)
			continue
		}

		h.transformAndStore(link, linkAddresses, ifDetails)
		ifs = append(ifs, ifDetails)
	}

	return ifs, nil
}

// LinuxInterfaceStatistics returns linux interface name/index with statistics data
type LinuxInterfaceStatistics struct {
	Name       string
	Index      int
	Statistics *netlink.LinkStatistics
}

// DumpInterfaceStatistics is an implementation of linux interface handler
func (h *NetLinkHandler) DumpInterfaceStatistics() ([]*LinuxInterfaceStatistics, error) {
	var ifs []*LinuxInterfaceStatistics

	ctx := nsplugin.NewNamespaceMgmtCtx()

	// Iterate over all known interfaces
	for _, ifName := range h.ifIndexes.GetMapping().ListNames() {
		_, meta, found := h.ifIndexes.LookupIdx(ifName)
		if !found {
			h.log.Warnf("Expected interface %s not found in the mapping", ifName)
			continue
		}
		if meta == nil || meta.Data == nil {
			h.log.Warnf("Expected interface %s metadata are missing", ifName)
			continue
		}

		// Use host name to dump interfaces via netlink
		link, _, err := h.dumpInterfaceData(meta.Data.HostIfName, h.nsHandler.IfNsToGeneric(meta.Data.Namespace), ctx)
		if err != nil {
			// Do not return error, read what is possible
			h.log.Errorf("failed to get interface %s data: %v", ifName, err)
			continue
		}

		if link == nil || link.Attrs() == nil {
			h.log.Warnf("Unable to get link data for interface %s", ifName)
			continue
		}

		linkAttrs := link.Attrs()

		// Fill data
		linuxStats := &LinuxInterfaceStatistics{
			Name:       linkAttrs.Name,
			Index:      linkAttrs.Index,
			Statistics: linkAttrs.Statistics,
		}

		ifs = append(ifs, linuxStats)
	}

	return ifs, nil
}

// Reads interface data and ip addresses from provided namespace
func (h *NetLinkHandler) dumpInterfaceData(ifName string, ns *nsplugin.Namespace, ctx *nsplugin.NamespaceMgmtCtx) (netlink.Link, []netlink.Addr, error) {
	revert, err := h.nsHandler.SwitchNamespace(ns, ctx)
	defer revert()

	if err != nil {
		// Do not return error here, since the interface namespace could be removed in the meantime
		h.log.Debugf("interface %s cannot be read, namespace is not accessible (err: %s)", ifName, err)
		return nil, nil, nil
	}
	link, err := h.GetLinkByName(ifName)
	if err != nil {
		return nil, nil, errors.Errorf("failed to get interface %s from namespace %s: %v", ifName, ns.Name, err)
	}
	linkAddrs, err := h.GetAddressList(ifName)
	if err != nil {
		return nil, nil, errors.Errorf("failed to get interface %s addresses: %v", ifName, err)
	}

	return link, linkAddrs, nil
}

// Transform link type interface to the interface details and store IP addresses to the proto modelled data
func (h *NetLinkHandler) transformAndStore(link netlink.Link, addr []netlink.Addr, ifDetails *LinuxInterfaceDetails) {
	// Set IP addresses to model-based data
	var ipAddrs []string
	for _, linkAddr := range addr {
		ipAddrs = append(ipAddrs, linkAddr.String())
	}
	if ifDetails.Interface == nil {
		ifDetails.Interface = &interfaces.LinuxInterfaces_Interface{
			IpAddresses: ipAddrs,
		}
	} else {
		ifDetails.Interface.IpAddresses = ipAddrs
	}

	// Metadata fields
	linkAttrs := link.Attrs()
	linuxMeta := &LinuxInterfaceMeta{
		Index:       linkAttrs.Index,
		Name:        linkAttrs.Name,
		Alias:       linkAttrs.Alias,
		OperState:   linkAttrs.OperState.String(),
		Flags:       linkAttrs.Flags.String(),
		Mtu:         linkAttrs.MTU,
		Type:        linkAttrs.EncapType,
		NetNsID:     linkAttrs.NetNsID,
		NumTxQueues: linkAttrs.NumTxQueues,
		TxQueueLen:  linkAttrs.TxQLen,
		NumRxQueues: linkAttrs.NumRxQueues,
	}
	if linkAttrs.HardwareAddr != nil {
		linuxMeta.MacAddr = linkAttrs.HardwareAddr.String()
	}
	ifDetails.Meta = linuxMeta
}
<|endoftext|>


<commit_before>package osin

import (
	"code.google.com/p/go-uuid/uuid"
	"encoding/base64"
)

// AuthorizeTokenGenDefault is the default authorization token generator
type AuthorizeTokenGenDefault struct {
}

// GenerateAuthorizeToken generates a base64-encoded UUID code
func (a *AuthorizeTokenGenDefault) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) {
	token := uuid.New()
	return base64.StdEncoding.EncodeToString([]byte(token)), nil
}

// AccessTokenGenDefault is the default authorization token generator
type AccessTokenGenDefault struct {
}

// GenerateAccessToken generates base64-encoded UUID access and refresh tokens
func (a *AccessTokenGenDefault) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
	accesstoken = uuid.New()
	accesstoken = base64.StdEncoding.EncodeToString([]byte(accesstoken))

	if generaterefresh {
		refreshtoken = uuid.New()
		refreshtoken = base64.StdEncoding.EncodeToString([]byte(refreshtoken))
	}
	return
}
<commit_msg>uuid: update dependency to new location<commit_after>package osin

import (
	"encoding/base64"

	"github.com/pborman/uuid"
)

// AuthorizeTokenGenDefault is the default authorization token generator
type AuthorizeTokenGenDefault struct {
}

// GenerateAuthorizeToken generates a base64-encoded UUID code
func (a *AuthorizeTokenGenDefault) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) {
	token := uuid.New()
	return base64.StdEncoding.EncodeToString([]byte(token)), nil
}

// AccessTokenGenDefault is the default authorization token generator
type AccessTokenGenDefault struct {
}

// GenerateAccessToken generates base64-encoded UUID access and refresh tokens
func (a *AccessTokenGenDefault) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) {
	accesstoken = uuid.New()
	accesstoken = base64.StdEncoding.EncodeToString([]byte(accesstoken))

	if generaterefresh {
		refreshtoken = uuid.New()
		refreshtoken = base64.StdEncoding.EncodeToString([]byte(refreshtoken))
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2016-2020 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"time"

	operatorOption "github.com/cilium/cilium/operator/option"
	"github.com/cilium/cilium/operator/watchers"
	"github.com/cilium/cilium/pkg/controller"
	"github.com/cilium/cilium/pkg/k8s"
	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
	k8sUtils "github.com/cilium/cilium/pkg/k8s/utils"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// enableCiliumEndpointSyncGC starts the node-singleton sweeper for
// CiliumEndpoint objects where the managing node is no longer running. These
// objects are created by the sync-to-k8s-ciliumendpoint controller on each
// Endpoint.
// The general steps are:
//   - list all CEPs in the cluster
//   - for each CEP
//       delete CEP if the corresponding pod does not exist
// CiliumEndpoint objects have the same name as the pod they represent
func enableCiliumEndpointSyncGC(once bool) {
	var (
		controllerName = "to-k8s-ciliumendpoint-gc"
		scopedLog      = log.WithField("controller", controllerName)
		gcInterval     time.Duration
		stopCh         = make(chan struct{})
	)

	ciliumClient := ciliumK8sClient.CiliumV2()

	if once {
		log.Info("Running the garbage collector only once to clean up leftover CiliumEndpoint custom resources...")
		gcInterval = 0
	} else {
		log.Info("Starting to garbage collect stale CiliumEndpoint custom resources...")
		gcInterval = operatorOption.Config.EndpointGCInterval
	}

	// This functions will block until the resources are synced with k8s.
	watchers.CiliumEndpointsInit(ciliumClient, stopCh)
	if !once {
		// If we are running this function "once" it means that we
		// will delete all CEPs in the cluster regardless of the pod
		// state.
		watchers.PodsInit(k8s.WatcherClient(), stopCh)
	}
	<-k8sCiliumNodesCacheSynced

	// this dummy manager is needed only to add this controller to the global list
	controller.NewManager().UpdateController(controllerName,
		controller.ControllerParams{
			RunInterval: gcInterval,
			DoFunc: func(ctx context.Context) error {
				return doCiliumEndpointSyncGC(ctx, once, stopCh, scopedLog)
			},
		})
}

func doCiliumEndpointSyncGC(ctx context.Context, once bool, stopCh chan struct{}, scopedLog *logrus.Entry) error {
	ciliumClient := ciliumK8sClient.CiliumV2()
	// For each CEP we fetched, check if we know about it
	for _, cepObj := range watchers.CiliumEndpointStore.List() {
		cep, ok := cepObj.(*cilium_v2.CiliumEndpoint)
		if !ok {
			log.WithField(logfields.Object, cepObj).
				Errorf("Saw %T object while expecting *cilium_v2.CiliumEndpoint", cepObj)
			continue
		}
		cepFullName := cep.Namespace + "/" + cep.Name
		scopedLog = scopedLog.WithFields(logrus.Fields{
			logfields.K8sPodName: cepFullName,
		})

		// If we are running this function "once" it means that we
		// will delete all CEPs in the cluster regardless of the pod
		// state therefore we won't even watch for the pod store.
		if !once {
			var podObj interface{}
			var err error
			exists := false
			podChecked := false
			for _, owner := range cep.ObjectMeta.OwnerReferences {
				switch owner.Kind {
				case "Pod":
					podObj, exists, err = watchers.PodStore.GetByKey(cepFullName)
					if err != nil {
						scopedLog.WithError(err).Warn("Unable to get pod from store")
					}
					podChecked = true
				case "CiliumNode":
					podObj, exists, err = ciliumNodeStore.GetByKey(owner.Name)
					if err != nil {
						scopedLog.WithError(err).Warn("Unable to get CiliumNode from store")
					}
				}
				// Stop looking when an existing owner has been found
				if exists {
					break
				}
			}
			if !exists && !podChecked {
				// Check for a Pod in case none of the owners existed
				// This keeps the old behavior even if OwnerReferences are missing
				podObj, exists, err = watchers.PodStore.GetByKey(cepFullName)
				if err != nil {
					scopedLog.WithError(err).Warn("Unable to get pod from store")
				}
			}
			if exists {
				switch pod := podObj.(type) {
				case *cilium_v2.CiliumNode:
					continue
				case *slim_corev1.Pod:
					// In Kubernetes Jobs, Pods can be left in Kubernetes until the Job
					// is deleted. If the Job is never deleted, Cilium will never receive a Pod
					// delete event, causing the IP to be left in the ipcache.
					// For this reason we should delete the ipcache entries whenever the pod
					// status is either PodFailed or PodSucceeded as it means the IP address
					// is no longer in use.
					if k8sUtils.IsPodRunning(pod.Status) {
						continue
					}
				default:
					log.WithField(logfields.Object, podObj).
						Errorf("Saw %T object while expecting *slim_corev1.Pod or *cilium_v2.CiliumNode", podObj)
					continue
				}
			}
		}
		// FIXME: this is fragile as we might have received the
		// CEP notification first but not the pod notification
		// so we need to have a similar mechanism that we have
		// for the keep alive of security identities.
		scopedLog = scopedLog.WithFields(logrus.Fields{
			logfields.EndpointID: cep.Status.ID,
		})
		scopedLog.Debug("Orphaned CiliumEndpoint is being garbage collected")
		PropagationPolicy := meta_v1.DeletePropagationBackground // because these are const strings but the API wants pointers
		err := ciliumClient.CiliumEndpoints(cep.Namespace).Delete(
			ctx,
			cep.Name,
			meta_v1.DeleteOptions{PropagationPolicy: &PropagationPolicy})
		if !k8serrors.IsNotFound(err) {
			scopedLog.WithError(err).Warning("Unable to delete orphaned CEP")
			return err
		}
	}
	// We have cleaned up all CEPs from Kubernetes so we can stop
	// the k8s watchers.
	if once {
		close(stopCh)
	}
	return nil
}
<commit_msg>operator: do not early return on CEP GC<commit_after>// Copyright 2016-2020 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"time"

	operatorOption "github.com/cilium/cilium/operator/option"
	"github.com/cilium/cilium/operator/watchers"
	"github.com/cilium/cilium/pkg/controller"
	"github.com/cilium/cilium/pkg/k8s"
	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
	k8sUtils "github.com/cilium/cilium/pkg/k8s/utils"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// enableCiliumEndpointSyncGC starts the node-singleton sweeper for
// CiliumEndpoint objects where the managing node is no longer running. These
// objects are created by the sync-to-k8s-ciliumendpoint controller on each
// Endpoint.
// The general steps are:
//   - list all CEPs in the cluster
//   - for each CEP
//       delete CEP if the corresponding pod does not exist
// CiliumEndpoint objects have the same name as the pod they represent
func enableCiliumEndpointSyncGC(once bool) {
	var (
		controllerName = "to-k8s-ciliumendpoint-gc"
		scopedLog      = log.WithField("controller", controllerName)
		gcInterval     time.Duration
		stopCh         = make(chan struct{})
	)

	ciliumClient := ciliumK8sClient.CiliumV2()

	if once {
		log.Info("Running the garbage collector only once to clean up leftover CiliumEndpoint custom resources...")
		gcInterval = 0
	} else {
		log.Info("Starting to garbage collect stale CiliumEndpoint custom resources...")
		gcInterval = operatorOption.Config.EndpointGCInterval
	}

	// This functions will block until the resources are synced with k8s.
	watchers.CiliumEndpointsInit(ciliumClient, stopCh)
	if !once {
		// If we are running this function "once" it means that we
		// will delete all CEPs in the cluster regardless of the pod
		// state.
		watchers.PodsInit(k8s.WatcherClient(), stopCh)
	}
	<-k8sCiliumNodesCacheSynced

	// this dummy manager is needed only to add this controller to the global list
	controller.NewManager().UpdateController(controllerName,
		controller.ControllerParams{
			RunInterval: gcInterval,
			DoFunc: func(ctx context.Context) error {
				return doCiliumEndpointSyncGC(ctx, once, stopCh, scopedLog)
			},
		})
}

func doCiliumEndpointSyncGC(ctx context.Context, once bool, stopCh chan struct{}, scopedLog *logrus.Entry) error {
	ciliumClient := ciliumK8sClient.CiliumV2()
	// For each CEP we fetched, check if we know about it
	for _, cepObj := range watchers.CiliumEndpointStore.List() {
		cep, ok := cepObj.(*cilium_v2.CiliumEndpoint)
		if !ok {
			log.WithField(logfields.Object, cepObj).
				Errorf("Saw %T object while expecting *cilium_v2.CiliumEndpoint", cepObj)
			continue
		}
		cepFullName := cep.Namespace + "/" + cep.Name
		scopedLog = scopedLog.WithFields(logrus.Fields{
			logfields.K8sPodName: cepFullName,
		})

		// If we are running this function "once" it means that we
		// will delete all CEPs in the cluster regardless of the pod
		// state therefore we won't even watch for the pod store.
		if !once {
			var podObj interface{}
			var err error
			exists := false
			podChecked := false
			for _, owner := range cep.ObjectMeta.OwnerReferences {
				switch owner.Kind {
				case "Pod":
					podObj, exists, err = watchers.PodStore.GetByKey(cepFullName)
					if err != nil {
						scopedLog.WithError(err).Warn("Unable to get pod from store")
					}
					podChecked = true
				case "CiliumNode":
					podObj, exists, err = ciliumNodeStore.GetByKey(owner.Name)
					if err != nil {
						scopedLog.WithError(err).Warn("Unable to get CiliumNode from store")
					}
				}
				// Stop looking when an existing owner has been found
				if exists {
					break
				}
			}
			if !exists && !podChecked {
				// Check for a Pod in case none of the owners existed
				// This keeps the old behavior even if OwnerReferences are missing
				podObj, exists, err = watchers.PodStore.GetByKey(cepFullName)
				if err != nil {
					scopedLog.WithError(err).Warn("Unable to get pod from store")
				}
			}
			if exists {
				switch pod := podObj.(type) {
				case *cilium_v2.CiliumNode:
					continue
				case *slim_corev1.Pod:
					// In Kubernetes Jobs, Pods can be left in Kubernetes until the Job
					// is deleted. If the Job is never deleted, Cilium will never receive a Pod
					// delete event, causing the IP to be left in the ipcache.
					// For this reason we should delete the ipcache entries whenever the pod
					// status is either PodFailed or PodSucceeded as it means the IP address
					// is no longer in use.
					if k8sUtils.IsPodRunning(pod.Status) {
						continue
					}
				default:
					log.WithField(logfields.Object, podObj).
						Errorf("Saw %T object while expecting *slim_corev1.Pod or *cilium_v2.CiliumNode", podObj)
					continue
				}
			}
		}
		// FIXME: this is fragile as we might have received the
		// CEP notification first but not the pod notification
		// so we need to have a similar mechanism that we have
		// for the keep alive of security identities.
		scopedLog = scopedLog.WithFields(logrus.Fields{
			logfields.EndpointID: cep.Status.ID,
		})
		scopedLog.Debug("Orphaned CiliumEndpoint is being garbage collected")
		PropagationPolicy := meta_v1.DeletePropagationBackground // because these are const strings but the API wants pointers
		err := ciliumClient.CiliumEndpoints(cep.Namespace).Delete(
			ctx,
			cep.Name,
			meta_v1.DeleteOptions{PropagationPolicy: &PropagationPolicy})
		if err != nil && !k8serrors.IsNotFound(err) {
			scopedLog.WithError(err).Warning("Unable to delete orphaned CEP")
			return err
		}
	}
	// We have cleaned up all CEPs from Kubernetes so we can stop
	// the k8s watchers.
	if once {
		close(stopCh)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 xgfone
//
// 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 option

import (
	"encoding/json"

	"github.com/xgfone/go-tools/types"
)

// BoolOption is an Option of the bool type.
type BoolOption struct {
	Option
}

// NewBoolOption returns a new BoolOption.
func NewBoolOption(o Option) BoolOption {
	if o == nil {
		o = None()
	}
	return BoolOption{Option: o}
}

// Scan converts src as bool to the inner value.
func (o BoolOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToBool(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o BoolOption) UnmarshalJSON(src []byte) (err error) {
	var v bool
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// StringOption is an Option of the string type.
type StringOption struct {
	Option
}

// NewStringOption returns a new StringOption.
func NewStringOption(o Option) StringOption {
	if o == nil {
		o = None()
	}
	return StringOption{Option: o}
}

// Scan converts src as string to the inner value.
func (o StringOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToString(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o StringOption) UnmarshalJSON(src []byte) (err error) {
	o.Reset(string(src))
	return
}

// IntOption is an Option of the int type.
type IntOption struct {
	Option
}

// NewIntOption returns a new IntOption.
func NewIntOption(o Option) IntOption {
	if o == nil {
		o = None()
	}
	return IntOption{Option: o}
}

// Scan converts src as int to the inner value.
func (o IntOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToInt(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o IntOption) UnmarshalJSON(src []byte) (err error) {
	var v int
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int8Option is an Option of the int8 type.
type Int8Option struct {
	Option
}

// NewInt8Option returns a new Int8Option.
func NewInt8Option(o Option) Int8Option {
	if o == nil {
		o = None()
	}
	return Int8Option{Option: o}
}

// Scan converts src as int8 to the inner value.
func (o Int8Option) Scan(src interface{}) error {
	v, err := types.ToInt64(src)
	if err == nil {
		o.Reset(int8(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int8Option) UnmarshalJSON(src []byte) (err error) {
	var v int8
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int16Option is an Option of the int16 type.
type Int16Option struct {
	Option
}

// NewInt16Option returns a new Int16Option.
func NewInt16Option(o Option) Int16Option {
	if o == nil {
		o = None()
	}
	return Int16Option{Option: o}
}

// Scan converts src as int16 to the inner value.
func (o Int16Option) Scan(src interface{}) error {
	v, err := types.ToInt64(src)
	if err == nil {
		o.Reset(int16(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int16Option) UnmarshalJSON(src []byte) (err error) {
	var v int16
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int32Option is an Option of the int32 type.
type Int32Option struct {
	Option
}

// NewInt32Option returns a new Int32Option.
func NewInt32Option(o Option) Int32Option {
	if o == nil {
		o = None()
	}
	return Int32Option{Option: o}
}

// Scan converts src as int32 to the inner value.
func (o Int32Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToInt32(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int32Option) UnmarshalJSON(src []byte) (err error) {
	var v int32
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int64Option is an Option of the int64 type.
type Int64Option struct {
	Option
}

// NewInt64Option returns a new Int64Option.
func NewInt64Option(o Option) Int64Option {
	if o == nil {
		o = None()
	}
	return Int64Option{Option: o}
}

// Scan converts src as int64 to the inner value.
func (o Int64Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToInt64(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int64Option) UnmarshalJSON(src []byte) (err error) {
	var v int64
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// UintOption is an Option of the uint type.
type UintOption struct {
	Option
}

// NewUintOption returns a new UintOption.
func NewUintOption(o Option) UintOption {
	if o == nil {
		o = None()
	}
	return UintOption{Option: o}
}

// Scan converts src as uint to the inner value.
func (o UintOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToUint(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o UintOption) UnmarshalJSON(src []byte) (err error) {
	var v uint
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint8Option is an Option of the uint8 type.
type Uint8Option struct {
	Option
}

// NewUint8Option returns a new Uint8Option.
func NewUint8Option(o Option) Uint8Option {
	if o == nil {
		o = None()
	}
	return Uint8Option{Option: o}
}

// Scan converts src as uint8 to the inner value.
func (o Uint8Option) Scan(src interface{}) error {
	v, err := types.ToUint64(src)
	if err == nil {
		o.Reset(uint8(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint8Option) UnmarshalJSON(src []byte) (err error) {
	var v uint8
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint16Option is an Option of the uint16 type.
type Uint16Option struct {
	Option
}

// NewUint16Option returns a new Uint16Option.
func NewUint16Option(o Option) Uint16Option {
	if o == nil {
		o = None()
	}
	return Uint16Option{Option: o}
}

// Scan converts src as uint16 to the inner value.
func (o Uint16Option) Scan(src interface{}) error {
	v, err := types.ToUint64(src)
	if err == nil {
		o.Reset(uint16(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint16Option) UnmarshalJSON(src []byte) (err error) {
	var v uint16
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint32Option is an Option of the uint32 type.
type Uint32Option struct {
	Option
}

// NewUint32Option returns a new Uint32Option.
func NewUint32Option(o Option) Uint32Option {
	if o == nil {
		o = None()
	}
	return Uint32Option{Option: o}
}

// Scan converts src as uint32 to the inner value.
func (o Uint32Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToUint32(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint32Option) UnmarshalJSON(src []byte) (err error) {
	var v uint32
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint64Option is an Option of the uint64 type.
type Uint64Option struct {
	Option
}

// NewUint64Option returns a new Uint64Option.
func NewUint64Option(o Option) Uint64Option {
	if o == nil {
		o = None()
	}
	return Uint64Option{Option: o}
}

// Scan converts src as uint64 to the inner value.
func (o Uint64Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToUint64(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint64Option) UnmarshalJSON(src []byte) (err error) {
	var v uint64
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Float64Option is an Option of the float64 type.
type Float64Option struct {
	Option
}

// NewFloat64Option returns a new Float64Option.
func NewFloat64Option(o Option) Float64Option {
	if o == nil {
		o = None()
	}
	return Float64Option{Option: o}
}

// Scan converts src as float64 to the inner value.
func (o Float64Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToFloat64(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Float64Option) UnmarshalJSON(src []byte) (err error) {
	var v float64
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// TimeOption is an Option of the time.Time type.
type TimeOption struct {
	Option
}

// NewTimeOption returns a new TimeOption.
func NewTimeOption(o Option) TimeOption {
	if o == nil {
		o = None()
	}
	return TimeOption{Option: o}
}

// Scan converts src as float64 to the inner value.
func (o TimeOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) {
		return types.ToTime(v, types.DateTimeLayout)
	})
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o TimeOption) UnmarshalJSON(src []byte) (err error) {
	v, err := types.ToTime(src, types.DateTimeLayout)
	if err == nil {
		o.Reset(v)
	}
	return
}

// LocalTimeOption is an Option of the time.Time type, but parsed as the localtime.
type LocalTimeOption struct {
	Option
}

// NewLocalTimeOption returns a new LocalTimeOption.
func NewLocalTimeOption(o Option) LocalTimeOption {
	if o == nil {
		o = None()
	}
	return LocalTimeOption{Option: o}
}

// Scan converts src as float64 to the inner value.
func (o LocalTimeOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) {
		return types.ToLocalTime(v, types.DateTimeLayout)
	})
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o LocalTimeOption) UnmarshalJSON(src []byte) (err error) {
	v, err := types.ToLocalTime(src, types.DateTimeLayout)
	if err == nil {
		o.Reset(v)
	}
	return
}
<commit_msg>remove LocalTimeOption<commit_after>// Copyright 2019 xgfone
//
// 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 option

import (
	"encoding/json"

	"github.com/xgfone/go-tools/types"
)

// BoolOption is an Option of the bool type.
type BoolOption struct {
	Option
}

// NewBoolOption returns a new BoolOption.
func NewBoolOption(o Option) BoolOption {
	if o == nil {
		o = None()
	}
	return BoolOption{Option: o}
}

// Scan converts src as bool to the inner value.
func (o BoolOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToBool(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o BoolOption) UnmarshalJSON(src []byte) (err error) {
	var v bool
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// StringOption is an Option of the string type.
type StringOption struct {
	Option
}

// NewStringOption returns a new StringOption.
func NewStringOption(o Option) StringOption {
	if o == nil {
		o = None()
	}
	return StringOption{Option: o}
}

// Scan converts src as string to the inner value.
func (o StringOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToString(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o StringOption) UnmarshalJSON(src []byte) (err error) {
	o.Reset(string(src))
	return
}

// IntOption is an Option of the int type.
type IntOption struct {
	Option
}

// NewIntOption returns a new IntOption.
func NewIntOption(o Option) IntOption {
	if o == nil {
		o = None()
	}
	return IntOption{Option: o}
}

// Scan converts src as int to the inner value.
func (o IntOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToInt(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o IntOption) UnmarshalJSON(src []byte) (err error) {
	var v int
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int8Option is an Option of the int8 type.
type Int8Option struct {
	Option
}

// NewInt8Option returns a new Int8Option.
func NewInt8Option(o Option) Int8Option {
	if o == nil {
		o = None()
	}
	return Int8Option{Option: o}
}

// Scan converts src as int8 to the inner value.
func (o Int8Option) Scan(src interface{}) error {
	v, err := types.ToInt64(src)
	if err == nil {
		o.Reset(int8(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int8Option) UnmarshalJSON(src []byte) (err error) {
	var v int8
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int16Option is an Option of the int16 type.
type Int16Option struct {
	Option
}

// NewInt16Option returns a new Int16Option.
func NewInt16Option(o Option) Int16Option {
	if o == nil {
		o = None()
	}
	return Int16Option{Option: o}
}

// Scan converts src as int16 to the inner value.
func (o Int16Option) Scan(src interface{}) error {
	v, err := types.ToInt64(src)
	if err == nil {
		o.Reset(int16(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int16Option) UnmarshalJSON(src []byte) (err error) {
	var v int16
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int32Option is an Option of the int32 type.
type Int32Option struct {
	Option
}

// NewInt32Option returns a new Int32Option.
func NewInt32Option(o Option) Int32Option {
	if o == nil {
		o = None()
	}
	return Int32Option{Option: o}
}

// Scan converts src as int32 to the inner value.
func (o Int32Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToInt32(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int32Option) UnmarshalJSON(src []byte) (err error) {
	var v int32
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Int64Option is an Option of the int64 type.
type Int64Option struct {
	Option
}

// NewInt64Option returns a new Int64Option.
func NewInt64Option(o Option) Int64Option {
	if o == nil {
		o = None()
	}
	return Int64Option{Option: o}
}

// Scan converts src as int64 to the inner value.
func (o Int64Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToInt64(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Int64Option) UnmarshalJSON(src []byte) (err error) {
	var v int64
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// UintOption is an Option of the uint type.
type UintOption struct {
	Option
}

// NewUintOption returns a new UintOption.
func NewUintOption(o Option) UintOption {
	if o == nil {
		o = None()
	}
	return UintOption{Option: o}
}

// Scan converts src as uint to the inner value.
func (o UintOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToUint(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o UintOption) UnmarshalJSON(src []byte) (err error) {
	var v uint
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint8Option is an Option of the uint8 type.
type Uint8Option struct {
	Option
}

// NewUint8Option returns a new Uint8Option.
func NewUint8Option(o Option) Uint8Option {
	if o == nil {
		o = None()
	}
	return Uint8Option{Option: o}
}

// Scan converts src as uint8 to the inner value.
func (o Uint8Option) Scan(src interface{}) error {
	v, err := types.ToUint64(src)
	if err == nil {
		o.Reset(uint8(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint8Option) UnmarshalJSON(src []byte) (err error) {
	var v uint8
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint16Option is an Option of the uint16 type.
type Uint16Option struct {
	Option
}

// NewUint16Option returns a new Uint16Option.
func NewUint16Option(o Option) Uint16Option {
	if o == nil {
		o = None()
	}
	return Uint16Option{Option: o}
}

// Scan converts src as uint16 to the inner value.
func (o Uint16Option) Scan(src interface{}) error {
	v, err := types.ToUint64(src)
	if err == nil {
		o.Reset(uint16(v))
	}
	return err
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint16Option) UnmarshalJSON(src []byte) (err error) {
	var v uint16
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint32Option is an Option of the uint32 type.
type Uint32Option struct {
	Option
}

// NewUint32Option returns a new Uint32Option.
func NewUint32Option(o Option) Uint32Option {
	if o == nil {
		o = None()
	}
	return Uint32Option{Option: o}
}

// Scan converts src as uint32 to the inner value.
func (o Uint32Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToUint32(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint32Option) UnmarshalJSON(src []byte) (err error) {
	var v uint32
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Uint64Option is an Option of the uint64 type.
type Uint64Option struct {
	Option
}

// NewUint64Option returns a new Uint64Option.
func NewUint64Option(o Option) Uint64Option {
	if o == nil {
		o = None()
	}
	return Uint64Option{Option: o}
}

// Scan converts src as uint64 to the inner value.
func (o Uint64Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToUint64(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Uint64Option) UnmarshalJSON(src []byte) (err error) {
	var v uint64
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// Float64Option is an Option of the float64 type.
type Float64Option struct {
	Option
}

// NewFloat64Option returns a new Float64Option.
func NewFloat64Option(o Option) Float64Option {
	if o == nil {
		o = None()
	}
	return Float64Option{Option: o}
}

// Scan converts src as float64 to the inner value.
func (o Float64Option) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) { return types.ToFloat64(v) })
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o Float64Option) UnmarshalJSON(src []byte) (err error) {
	var v float64
	if err = json.Unmarshal(src, &v); err == nil {
		o.Reset(v)
	}
	return
}

// TimeOption is an Option of the time.Time type.
type TimeOption struct {
	Option
}

// NewTimeOption returns a new TimeOption.
func NewTimeOption(o Option) TimeOption {
	if o == nil {
		o = None()
	}
	return TimeOption{Option: o}
}

// Scan converts src as float64 to the inner value.
func (o TimeOption) Scan(src interface{}) error {
	return o.ConvertTo(src, func(v interface{}) (interface{}, error) {
		return types.ToTime(v, types.DateTimeLayout)
	})
}

// UnmarshalJSON implements the interface json.Unmarshaler.
func (o TimeOption) UnmarshalJSON(src []byte) (err error) {
	v, err := types.ToTime(src, types.DateTimeLayout)
	if err == nil {
		o.Reset(v)
	}
	return
}
<|endoftext|>


<commit_before>package hookshot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
)

type payload struct {
	event string `json:"event"`
}

func Test_Router(t *testing.T) {
	tests := []struct {
		secret    string
		event     string
		body      string
		signature string

		status int
	}{
		{
			secret:    "1234",
			event:     "",
			body:      `{"event":"data"}`,
			signature: "invalid",
			status:    404,
		},
		{
			secret:    "1234",
			event:     "foobar",
			body:      `{"event":"data"}`,
			signature: "invalid",
			status:    404,
		},
		{
			secret:    "1234",
			event:     "deployment",
			body:      `{"event":"data"}`,
			signature: "invalid",
			status:    403,
		},
		{
			secret:    "1234",
			event:     "deployment",
			body:      `{"event":"data"}`,
			signature: "sha1=ade133892a181fba3a21c163cd5cbc3f5f8e915c",
			status:    200,
		},
		{
			secret: "",
			event:  "deployment",
			body:   `{"event":"data"}`,
			status: 200,
		},
	}

	for _, tt := range tests {
		router := NewRouter(tt.secret)

		router.HandleFunc("deployment", func(w http.ResponseWriter, r *http.Request) {
			var p payload
			err := json.NewDecoder(r.Body).Decode(&p)

			if err != nil {
				t.Fatalf("Could not read from request body: %v", err)
			}

			w.WriteHeader(200)
			w.Write([]byte("ok\n"))
		})

		resp := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/", bytes.NewReader([]byte(tt.body)))

		if tt.event != "" {
			req.Header.Set("X-GitHub-Event", tt.event)
		}

		if tt.signature != "" {
			req.Header.Set("X-Hub-Signature", tt.signature)
		}

		router.ServeHTTP(resp, req)

		if resp.Code != tt.status {
			t.Errorf("resp.Code = %v; want %v", resp.Code, tt.status)
		}

		expectedBody := ""
		switch tt.status {
		case 200:
			expectedBody = "ok\n"
		case 404:
			expectedBody = "404 page not found\n"
		case 403:
			expectedBody = "The provided signature in the X-Hub-Signature header does not match.\n"
		}

		if resp.Body.String() != expectedBody {
			t.Errorf("resp.Body = %q; want %q", resp.Body.String(), expectedBody)
		}
	}
}

func Test_Signature(t *testing.T) {
	tests := []struct {
		in     string
		secret string

		signature string
	}{
		{
			`{"event":"data"}`,
			"1234",
			"ade133892a181fba3a21c163cd5cbc3f5f8e915c",
		},
	}

	for _, tt := range tests {
		signature := Signature([]byte(tt.in), tt.secret)

		if signature != tt.signature {
			t.Errorf("Signature(%q, %q) => %q; want %q", tt.in, tt.secret, signature, tt.signature)
		}
	}
}

func ExampleRouter_HandleFunc() {
	r := NewRouter("secret")
	r.HandleFunc("ping", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`pong`))
	})

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", bytes.NewBufferString(`{"data":"foo"}`))
	req.Header.Set("X-GitHub-Event", "ping")
	req.Header.Set("X-Hub-Signature", "sha1=b3dc4e9a2d727ee1e60bb6828c2dcef88b5ec970")

	r.ServeHTTP(res, req)

	fmt.Print(res.Body)
	// Output: pong
}
<commit_msg>Add tests.<commit_after>package hookshot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
)

type payload struct {
	event string `json:"event"`
}

func Test_Router(t *testing.T) {
	tests := []struct {
		secret    string
		event     string
		body      string
		signature string

		status int
	}{
		{
			secret:    "1234",
			event:     "",
			body:      `{"event":"data"}`,
			signature: "invalid",
			status:    404,
		},
		{
			secret:    "1234",
			event:     "foobar",
			body:      `{"event":"data"}`,
			signature: "invalid",
			status:    404,
		},
		{
			secret:    "1234",
			event:     "deployment",
			body:      `{"event":"data"}`,
			signature: "invalid",
			status:    403,
		},
		{
			secret:    "1234",
			event:     "deployment",
			body:      `{"event":"data"}`,
			signature: "sha1=ade133892a181fba3a21c163cd5cbc3f5f8e915c",
			status:    200,
		},
		{
			secret: "",
			event:  "deployment",
			body:   `{"event":"data"}`,
			status: 200,
		},
	}

	for _, tt := range tests {
		router := NewRouter(tt.secret)

		router.HandleFunc("deployment", func(w http.ResponseWriter, r *http.Request) {
			var p payload
			err := json.NewDecoder(r.Body).Decode(&p)

			if err != nil {
				t.Fatalf("Could not read from request body: %v", err)
			}

			w.WriteHeader(200)
			w.Write([]byte("ok\n"))
		})

		resp := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/", bytes.NewReader([]byte(tt.body)))

		if tt.event != "" {
			req.Header.Set("X-GitHub-Event", tt.event)
		}

		if tt.signature != "" {
			req.Header.Set("X-Hub-Signature", tt.signature)
		}

		router.ServeHTTP(resp, req)

		if resp.Code != tt.status {
			t.Errorf("resp.Code => %v; want %v", resp.Code, tt.status)
		}

		expectedBody := ""
		switch tt.status {
		case 200:
			expectedBody = "ok\n"
		case 404:
			expectedBody = "404 page not found\n"
		case 403:
			expectedBody = "The provided signature in the X-Hub-Signature header does not match.\n"
		}

		if resp.Body.String() != expectedBody {
			t.Errorf("resp.Body => %q; want %q", resp.Body.String(), expectedBody)
		}

		if resp.Header().Get("X-Calculated-Signature") != "" {
			t.Errorf("resp.Header[X-Calculated-Signature] => %q; want %q", resp.Header().Get("X-Calculated-Signature"), "")
		}
	}
}

func Test_Signature(t *testing.T) {
	tests := []struct {
		in     string
		secret string

		signature string
	}{
		{
			`{"event":"data"}`,
			"1234",
			"ade133892a181fba3a21c163cd5cbc3f5f8e915c",
		},
	}

	for _, tt := range tests {
		signature := Signature([]byte(tt.in), tt.secret)

		if signature != tt.signature {
			t.Errorf("Signature(%q, %q) => %q; want %q", tt.in, tt.secret, signature, tt.signature)
		}
	}
}

func ExampleRouter_HandleFunc() {
	r := NewRouter("secret")
	r.HandleFunc("ping", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`pong`))
	})

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", bytes.NewBufferString(`{"data":"foo"}`))
	req.Header.Set("X-GitHub-Event", "ping")
	req.Header.Set("X-Hub-Signature", "sha1=b3dc4e9a2d727ee1e60bb6828c2dcef88b5ec970")

	r.ServeHTTP(res, req)

	fmt.Print(res.Body)
	// Output: pong
}
<|endoftext|>


<commit_before>package tumblr

import (
	"math/rand"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/gosimple/slug"
	"github.com/pkg/errors"
	"github.com/rollbar/rollbar-go"
)

// MaxKeywords is the maximum number of keywords that can be returned by a board
const (
	MaxKeywords   = 20
	imageRootPath = "https://img.reaction.pics/file/reaction-pics/"
)

// Post is a representation of a single tumblr post
type Post struct {
	ID    int64  `json:"id"`
	Title string `json:"title"`
	URL   string `json:"url"`
	Image string `json:"image"`
	Likes int64  `json:"likes"`
}

// PostJSON is a representation of Post for creating JSON values
type PostJSON struct {
	Post
	InternalURL string `json:"internalURL"`
}

// InternalURL returns the path to the post
func (p Post) InternalURL() string {
	slug := slug.Make(p.Title)
	if len(slug) > 30 {
		slug = slug[0:30]
	}
	return "/post/" + strconv.FormatInt(p.ID, 10) + "/" + slug
}

// ToJSONStruct builds a PostJSON based on the Post
func (p Post) ToJSONStruct() PostJSON {
	return PostJSON{
		Post:        p,
		InternalURL: p.InternalURL(),
	}
}

// CSVToPost converts a CSV row into a Post
func CSVToPost(row []string) *Post {
	id, err := strconv.ParseInt(row[0], 10, 64)
	if err != nil {
		err = errors.Wrapf(err, "Cannot parse id for %s", row[0])
		id = 0
	}

	imageURL := imageRootPath + row[3]

	likes, err := strconv.ParseInt(row[4], 10, 64)
	if err != nil {
		err = errors.Wrapf(err, "Cannot parse likes for %s", row[4])
		rollbar.Error(rollbar.ERR, err)
		likes = 0
	}
	post := Post{
		ID:    id,
		Title: row[1],
		URL:   row[2],
		Image: imageURL,
		Likes: likes,
	}
	return &post
}

// Board is a container for Posts that offers serialization, sorting, and
// parallelization
type Board struct {
	Posts []Post
	mut   *sync.RWMutex
}

// InitializeBoard means to create a new board and start writing reading saved
// posts into it
func InitializeBoard() *Board {
	board := NewBoard([]Post{})
	go board.populateBoardFromCSV()
	return &board
}

// NewBoard creates a Board from an array of Posts
func NewBoard(p []Post) Board {
	return Board{
		Posts: p,
		mut:   &sync.RWMutex{},
	}
}

func (b *Board) populateBoardFromCSV() {
	b.mut.Lock()
	posts := ReadPostsFromCSV(getCSVPath(false))
	b.Posts = append(b.Posts, posts...)
	b.mut.Unlock()
	b.SortPostsByLikes()
}

// AddPost adds a single post to the board and sorts it
func (b *Board) AddPost(p Post) {
	b.mut.Lock()
	defer b.mut.Unlock()
	for i := 0; i < len(b.Posts); i++ {
		if b.Posts[i].Title == p.Title {
			return
		}
	}
	b.Posts = append(b.Posts, p)
}

// PostsToJSON converts a Post into a JSON string
func (b Board) PostsToJSON() *[]PostJSON {
	b.mut.RLock()
	defer b.mut.RUnlock()
	postsJSON := make([]PostJSON, len(b.Posts))
	for i := 0; i < len(b.Posts); i++ {
		postsJSON[i] = b.Posts[i].ToJSONStruct()
	}
	return &postsJSON
}

// FilterBoard returns a new Board with a subset of posts filtered by a string
func (b Board) FilterBoard(query string) *Board {
	b.mut.RLock()
	defer b.mut.RUnlock()
	selectedPosts := []Post{}
	for _, post := range b.Posts {
		postData := strings.ToLower(post.Title)
		if strings.Contains(postData, query) {
			selectedPosts = append(selectedPosts, post)
		}
	}
	board := NewBoard(selectedPosts)
	return &board
}

// GetPostByID returns a post that matches the postID
func (b Board) GetPostByID(postID int64) *Post {
	b.mut.RLock()
	defer b.mut.RUnlock()
	for _, post := range b.Posts {
		if post.ID == postID {
			return &post
		}
	}
	return nil
}

// LimitBoard modifies the current board with maxResults posts starting at offset
func (b *Board) LimitBoard(offset, maxResults int) {
	if offset > len(b.Posts) {
		offset = len(b.Posts)
	}
	endIndex := offset + maxResults
	if endIndex > len(b.Posts) {
		endIndex = len(b.Posts)
	}
	b.Posts = b.Posts[offset:endIndex]
}

// SortPostsByLikes sorts Posts in reverse number of likes order
func (b *Board) SortPostsByLikes() {
	b.mut.Lock()
	defer b.mut.Unlock()
	sort.Sort(sort.Reverse(SortByLikes(b.Posts)))
}

// SortByLikes is an interface for Sorting
type SortByLikes []Post

func (a SortByLikes) Len() int           { return len(a) }
func (a SortByLikes) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a SortByLikes) Less(i, j int) bool { return a[i].Likes < a[j].Likes }

// RandomizePosts will shuffle the current Board's posts
func (b *Board) RandomizePosts() {
	b.mut.Lock()
	defer b.mut.Unlock()
	rand.Shuffle(len(b.Posts), func(i, j int) {
		b.Posts[i], b.Posts[j] = b.Posts[j], b.Posts[i]
	})
}

// URLs returns an array of URLs of all the posts
func (b Board) URLs() []string {
	b.mut.RLock()
	defer b.mut.RUnlock()
	urls := []string{}
	for _, post := range b.Posts {
		urls = append(urls, post.InternalURL())
	}
	return urls
}

// Keywords returns the most popular words in posts
func (b Board) Keywords() []string {
	b.mut.RLock()
	defer b.mut.RUnlock()
	words := map[string]int{}
	for _, post := range b.Posts {
		for _, word := range strings.Fields(post.Title) {
			if len(word) > 4 {
				words[word]++
			}
		}
	}
	// Reuse Board sort
	board := NewBoard([]Post{})
	for word, count := range words {
		board.AddPost(Post{Title: word, Likes: int64(count)})
	}
	board.SortPostsByLikes()
	count := 0
	keywords := []string{}
	for _, post := range board.Posts {
		keywords = append(keywords, post.Title)
		count++
		if count >= MaxKeywords {
			break
		}
	}
	return keywords
}
<commit_msg>Log unparseable IDs<commit_after>package tumblr

import (
	"math/rand"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/gosimple/slug"
	"github.com/pkg/errors"
	"github.com/rollbar/rollbar-go"
)

// MaxKeywords is the maximum number of keywords that can be returned by a board
const (
	MaxKeywords   = 20
	imageRootPath = "https://img.reaction.pics/file/reaction-pics/"
)

// Post is a representation of a single tumblr post
type Post struct {
	ID    int64  `json:"id"`
	Title string `json:"title"`
	URL   string `json:"url"`
	Image string `json:"image"`
	Likes int64  `json:"likes"`
}

// PostJSON is a representation of Post for creating JSON values
type PostJSON struct {
	Post
	InternalURL string `json:"internalURL"`
}

// InternalURL returns the path to the post
func (p Post) InternalURL() string {
	slug := slug.Make(p.Title)
	if len(slug) > 30 {
		slug = slug[0:30]
	}
	return "/post/" + strconv.FormatInt(p.ID, 10) + "/" + slug
}

// ToJSONStruct builds a PostJSON based on the Post
func (p Post) ToJSONStruct() PostJSON {
	return PostJSON{
		Post:        p,
		InternalURL: p.InternalURL(),
	}
}

// CSVToPost converts a CSV row into a Post
func CSVToPost(row []string) *Post {
	id, err := strconv.ParseInt(row[0], 10, 64)
	if err != nil {
		err = errors.Wrapf(err, "Cannot parse id for %s", row[0])
		rollbar.Error(rollbar.ERR, err)
		id = 0
	}

	imageURL := imageRootPath + row[3]

	likes, err := strconv.ParseInt(row[4], 10, 64)
	if err != nil {
		err = errors.Wrapf(err, "Cannot parse likes for %s", row[4])
		rollbar.Error(rollbar.ERR, err)
		likes = 0
	}
	post := Post{
		ID:    id,
		Title: row[1],
		URL:   row[2],
		Image: imageURL,
		Likes: likes,
	}
	return &post
}

// Board is a container for Posts that offers serialization, sorting, and
// parallelization
type Board struct {
	Posts []Post
	mut   *sync.RWMutex
}

// InitializeBoard means to create a new board and start writing reading saved
// posts into it
func InitializeBoard() *Board {
	board := NewBoard([]Post{})
	go board.populateBoardFromCSV()
	return &board
}

// NewBoard creates a Board from an array of Posts
func NewBoard(p []Post) Board {
	return Board{
		Posts: p,
		mut:   &sync.RWMutex{},
	}
}

func (b *Board) populateBoardFromCSV() {
	b.mut.Lock()
	posts := ReadPostsFromCSV(getCSVPath(false))
	b.Posts = append(b.Posts, posts...)
	b.mut.Unlock()
	b.SortPostsByLikes()
}

// AddPost adds a single post to the board and sorts it
func (b *Board) AddPost(p Post) {
	b.mut.Lock()
	defer b.mut.Unlock()
	for i := 0; i < len(b.Posts); i++ {
		if b.Posts[i].Title == p.Title {
			return
		}
	}
	b.Posts = append(b.Posts, p)
}

// PostsToJSON converts a Post into a JSON string
func (b Board) PostsToJSON() *[]PostJSON {
	b.mut.RLock()
	defer b.mut.RUnlock()
	postsJSON := make([]PostJSON, len(b.Posts))
	for i := 0; i < len(b.Posts); i++ {
		postsJSON[i] = b.Posts[i].ToJSONStruct()
	}
	return &postsJSON
}

// FilterBoard returns a new Board with a subset of posts filtered by a string
func (b Board) FilterBoard(query string) *Board {
	b.mut.RLock()
	defer b.mut.RUnlock()
	selectedPosts := []Post{}
	for _, post := range b.Posts {
		postData := strings.ToLower(post.Title)
		if strings.Contains(postData, query) {
			selectedPosts = append(selectedPosts, post)
		}
	}
	board := NewBoard(selectedPosts)
	return &board
}

// GetPostByID returns a post that matches the postID
func (b Board) GetPostByID(postID int64) *Post {
	b.mut.RLock()
	defer b.mut.RUnlock()
	for _, post := range b.Posts {
		if post.ID == postID {
			return &post
		}
	}
	return nil
}

// LimitBoard modifies the current board with maxResults posts starting at offset
func (b *Board) LimitBoard(offset, maxResults int) {
	if offset > len(b.Posts) {
		offset = len(b.Posts)
	}
	endIndex := offset + maxResults
	if endIndex > len(b.Posts) {
		endIndex = len(b.Posts)
	}
	b.Posts = b.Posts[offset:endIndex]
}

// SortPostsByLikes sorts Posts in reverse number of likes order
func (b *Board) SortPostsByLikes() {
	b.mut.Lock()
	defer b.mut.Unlock()
	sort.Sort(sort.Reverse(SortByLikes(b.Posts)))
}

// SortByLikes is an interface for Sorting
type SortByLikes []Post

func (a SortByLikes) Len() int           { return len(a) }
func (a SortByLikes) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a SortByLikes) Less(i, j int) bool { return a[i].Likes < a[j].Likes }

// RandomizePosts will shuffle the current Board's posts
func (b *Board) RandomizePosts() {
	b.mut.Lock()
	defer b.mut.Unlock()
	rand.Shuffle(len(b.Posts), func(i, j int) {
		b.Posts[i], b.Posts[j] = b.Posts[j], b.Posts[i]
	})
}

// URLs returns an array of URLs of all the posts
func (b Board) URLs() []string {
	b.mut.RLock()
	defer b.mut.RUnlock()
	urls := []string{}
	for _, post := range b.Posts {
		urls = append(urls, post.InternalURL())
	}
	return urls
}

// Keywords returns the most popular words in posts
func (b Board) Keywords() []string {
	b.mut.RLock()
	defer b.mut.RUnlock()
	words := map[string]int{}
	for _, post := range b.Posts {
		for _, word := range strings.Fields(post.Title) {
			if len(word) > 4 {
				words[word]++
			}
		}
	}
	// Reuse Board sort
	board := NewBoard([]Post{})
	for word, count := range words {
		board.AddPost(Post{Title: word, Likes: int64(count)})
	}
	board.SortPostsByLikes()
	count := 0
	keywords := []string{}
	for _, post := range board.Posts {
		keywords = append(keywords, post.Title)
		count++
		if count >= MaxKeywords {
			break
		}
	}
	return keywords
}
<|endoftext|>


<commit_before>package tutum

import (
	"fmt"
	"os"
	"os/user"
	"strings"

	"github.com/BurntSushi/toml"
)

var (
	User       string
	ApiKey     string
	AuthHeader string
	BaseUrl    = "https://dashboard.tutum.co/api/v1/"
	StreamUrl  = "wss://stream.tutum.co:443/v1/"
	version    = "0.16.0"
)

type config map[string]Auth

type Auth struct {
	User   string
	Apikey string
}

type TutumObject interface {
	Start() error
	Stop() error
	Redeploy() error
	Terminate() error
	Deploy() error
	Upgrade() error
	Update([]byte) error
}

func init() {
	LoadAuth()
}

func LoadAuth() error {
	if User != "" && ApiKey != "" {
		return nil
	} else {
		if os.Getenv("TUTUM_AUTH") != "" {
			AuthHeader = os.Getenv("TUTUM_AUTH")
		}
	}

	// Process ~/.tutum configuration file first
	if usr, err := user.Current(); err == nil {
		var conf config
		confFilePath := usr.HomeDir + "/.tutum"
		if _, err := os.Stat(confFilePath); !os.IsNotExist(err) {
			if _, err := toml.DecodeFile(confFilePath, &conf); err == nil {
				if conf["auth"].User != "" && conf["auth"].Apikey != "" {
					User = conf["auth"].User
					ApiKey = conf["auth"].Apikey
					return nil
				}
			} else {
				return fmt.Errorf("Malformed Tutum configuration file found at %s: %s", confFilePath, err)
			}
		}
	}

	// Load environment variables as an alternative option
	if os.Getenv("TUTUM_USER") != "" && os.Getenv("TUTUM_APIKEY") != "" {
		User = os.Getenv("TUTUM_USER")
		ApiKey = os.Getenv("TUTUM_APIKEY")
		return nil
	}

	return fmt.Errorf("Couldn't find any Tutum credentials in ~/.tutum or environment variables TUTUM_USER and TUTUM_APIKEY")
}

func IsAuthenticated() bool {
	return ((User != "" && ApiKey != "") || os.Getenv("TUTUM_AUTH") != "")
}

func FetchByResourceUri(id string) interface{} {
	words := strings.Split(id, "/")
	switch words[3] {
	case "action":
		action, err := GetAction(id)

		if err != nil {
			return err
		}

		return action
	case "nodecluster":
		nodecluster, err := GetNodeCluster(id)

		if err != nil {
			return err
		}

		return nodecluster
	case "provider":
		provider, err := GetProvider(id)

		if err != nil {
			return err
		}

		return provider
	case "region":
		region, err := GetRegion(id)

		if err != nil {
			return err
		}

		return region
	case "service":
		service, err := GetService(id)

		if err != nil {
			return err
		}

		return service
	case "stack":
		stack, err := GetStack(id)

		if err != nil {
			return err
		}

		return stack
	case "volume":
		volume, err := GetVolume(id)

		if err != nil {
			return err
		}

		return volume
	case "volumegroup":
		volumegroup, err := GetVolumeGroup(id)

		if err != nil {
			return err
		}

		return volumegroup
	case "node":
		node, err := GetNode(id)

		if err != nil {
			return err
		}

		return node
	case "container":
		container, err := GetContainer(id)

		if err != nil {
			return err
		}

		return container
	}
	return 0
}
<commit_msg>handle new .tutum format<commit_after>package tutum

import (
	"fmt"
	"os"
	"os/user"
	"strings"

	"github.com/BurntSushi/toml"
)

var (
	User       string
	ApiKey     string
	BasicAuth  string
	AuthHeader string
	BaseUrl    = "https://dashboard.tutum.co/api/v1/"
	StreamUrl  = "wss://stream.tutum.co:443/v1/"
	version    = "0.16.0"
)

type config map[string]Auth

type Auth struct {
	User       string
	Apikey     string
	Basic_auth string
}

type TutumObject interface {
	Start() error
	Stop() error
	Redeploy() error
	Terminate() error
	Deploy() error
	Upgrade() error
	Update([]byte) error
}

func init() {
	LoadAuth()
}

func LoadAuth() error {
	if User != "" && ApiKey != "" {
		return nil
	} else {
		if os.Getenv("TUTUM_AUTH") != "" {
			AuthHeader = os.Getenv("TUTUM_AUTH")
		}
	}

	// Process ~/.tutum configuration file first
	if usr, err := user.Current(); err == nil {
		var conf config
		confFilePath := usr.HomeDir + "/.tutum"
		if _, err := os.Stat(confFilePath); !os.IsNotExist(err) {
			if _, err := toml.DecodeFile(confFilePath, &conf); err == nil {
				if conf["auth"].User != "" && conf["auth"].Apikey != "" {
					User = conf["auth"].User
					ApiKey = conf["auth"].Apikey
					return nil
				} else {
					if conf["auth"].Basic_auth != "" {
						BasicAuth = conf["auth"].Basic_auth
						AuthHeader = fmt.Sprintf("Basic %s", conf["auth"].Basic_auth)
						return nil
					}
				}
			} else {
				return fmt.Errorf("Malformed Tutum configuration file found at %s: %s", confFilePath, err)
			}
		}
	}

	// Load environment variables as an alternative option
	if os.Getenv("TUTUM_USER") != "" && os.Getenv("TUTUM_APIKEY") != "" {
		User = os.Getenv("TUTUM_USER")
		ApiKey = os.Getenv("TUTUM_APIKEY")
		return nil
	}

	return fmt.Errorf("Couldn't find any Tutum credentials in ~/.tutum or environment variables TUTUM_USER and TUTUM_APIKEY")
}

func IsAuthenticated() bool {
	return ((User != "" && ApiKey != "") || BasicAuth != "" || os.Getenv("TUTUM_AUTH") != "")
}

func FetchByResourceUri(id string) interface{} {
	words := strings.Split(id, "/")
	switch words[3] {
	case "action":
		action, err := GetAction(id)

		if err != nil {
			return err
		}

		return action
	case "nodecluster":
		nodecluster, err := GetNodeCluster(id)

		if err != nil {
			return err
		}

		return nodecluster
	case "provider":
		provider, err := GetProvider(id)

		if err != nil {
			return err
		}

		return provider
	case "region":
		region, err := GetRegion(id)

		if err != nil {
			return err
		}

		return region
	case "service":
		service, err := GetService(id)

		if err != nil {
			return err
		}

		return service
	case "stack":
		stack, err := GetStack(id)

		if err != nil {
			return err
		}

		return stack
	case "volume":
		volume, err := GetVolume(id)

		if err != nil {
			return err
		}

		return volume
	case "volumegroup":
		volumegroup, err := GetVolumeGroup(id)

		if err != nil {
			return err
		}

		return volumegroup
	case "node":
		node, err := GetNode(id)

		if err != nil {
			return err
		}

		return node
	case "container":
		container, err := GetContainer(id)

		if err != nil {
			return err
		}

		return container
	}
	return 0
}
<|endoftext|>


<commit_before>// Copyright 2010 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.

// The suffixarray package implements substring search in logarithmic time
// using an in-memory suffix array.
//
// Example use:
//
//	// create index for some data
//	index := suffixarray.New(data)
//
//	// lookup byte slice s
//	offsets1 := index.Lookup(s, -1) // the list of all indices where s occurs in data
//	offsets2 := index.Lookup(s, 3)  // the list of at most 3 indices where s occurs in data
//
package suffixarray

import (
	"bytes"
	"container/vector"
	"sort"
)

// BUG(gri): For larger data (10MB) which contains very long (say 100000)
// contiguous sequences of identical bytes, index creation time will be extremely slow.

// TODO(gri): Use a more sophisticated algorithm to create the suffix array.

// Index implements a suffix array for fast substring search.
type Index struct {
	data []byte
	sa   []int // suffix array for data
}

// New creates a new Index for data.
// Index creation time is approximately O(N*log(N)) for N = len(data).
//
func New(data []byte) *Index {
	sa := make([]int, len(data))
	for i, _ := range sa {
		sa[i] = i
	}
	x := &Index{data, sa}
	sort.Sort((*index)(x))
	return x
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

// Binary search according to "A Method of Programming", E.W. Dijkstra.
func (x *Index) search(s []byte) int {
	i, j := 0, len(x.sa)
	// i < j for non-empty x
	for i+1 < j {
		// 0 <= i < j <= len(x.sa) && (x.at(i) <= s < x.at(j) || (s is not in x))
		h := i + (j-i)/2 // i < h < j
		if bytes.Compare(x.at(h), s) <= 0 {
			i = h
		} else { // s < x.at(h)
			j = h
		}
	}
	// i+1 == j for non-empty x
	return i
}

// Lookup returns an unsorted list of at most n indices where the byte string s
// occurs in the indexed data. If n < 0, all occurrences are returned.
// The result is nil if s is empty, s is not found, or n == 0.
// Lookup time is O((log(N) + len(result))*len(s)) where N is the
// size of the indexed data.
//
func (x *Index) Lookup(s []byte, n int) []int {
	var res vector.IntVector

	if len(s) > 0 && n != 0 {
		// find matching suffix index i
		i := x.search(s)
		// x.at(i) <= s < x.at(i+1)

		// ignore the first suffix if it is < s
		if i < len(x.sa) && bytes.Compare(x.at(i), s) < 0 {
			i++
		}

		// collect the following suffixes with matching prefixes
		for (n < 0 || len(res) < n) && i < len(x.sa) && bytes.HasPrefix(x.at(i), s) {
			res.Push(x.sa[i])
			i++
		}
	}

	return res
}

// index is used to hide the sort.Interface
type index Index

func (x *index) Len() int           { return len(x.sa) }
func (x *index) Less(i, j int) bool { return bytes.Compare(x.at(i), x.at(j)) < 0 }
func (x *index) Swap(i, j int)      { x.sa[i], x.sa[j] = x.sa[j], x.sa[i] }
func (a *index) at(i int) []byte    { return a.data[a.sa[i]:] }
<commit_msg>index/suffixarray: use sort.Search<commit_after>// Copyright 2010 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.

// The suffixarray package implements substring search in logarithmic time
// using an in-memory suffix array.
//
// Example use:
//
//	// create index for some data
//	index := suffixarray.New(data)
//
//	// lookup byte slice s
//	offsets1 := index.Lookup(s, -1) // the list of all indices where s occurs in data
//	offsets2 := index.Lookup(s, 3)  // the list of at most 3 indices where s occurs in data
//
package suffixarray

import (
	"bytes"
	"container/vector"
	"sort"
)

// BUG(gri): For larger data (10MB) which contains very long (say 100000)
// contiguous sequences of identical bytes, index creation time will be extremely slow.

// TODO(gri): Use a more sophisticated algorithm to create the suffix array.

// Index implements a suffix array for fast substring search.
type Index struct {
	data []byte
	sa   []int // suffix array for data
}

// New creates a new Index for data.
// Index creation time is approximately O(N*log(N)) for N = len(data).
//
func New(data []byte) *Index {
	sa := make([]int, len(data))
	for i, _ := range sa {
		sa[i] = i
	}
	x := &Index{data, sa}
	sort.Sort((*index)(x))
	return x
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) search(s []byte) int {
	return sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
}

// Lookup returns an unsorted list of at most n indices where the byte string s
// occurs in the indexed data. If n < 0, all occurrences are returned.
// The result is nil if s is empty, s is not found, or n == 0.
// Lookup time is O((log(N) + len(result))*len(s)) where N is the
// size of the indexed data.
//
func (x *Index) Lookup(s []byte, n int) []int {
	var res vector.IntVector

	if len(s) > 0 && n != 0 {
		// find matching suffix index i
		i := x.search(s)
		// x.at(i-1) < s <= x.at(i)

		// collect the following suffixes with matching prefixes
		for (n < 0 || len(res) < n) && i < len(x.sa) && bytes.HasPrefix(x.at(i), s) {
			res.Push(x.sa[i])
			i++
		}
	}

	return res
}

// index is used to hide the sort.Interface
type index Index

func (x *index) Len() int           { return len(x.sa) }
func (x *index) Less(i, j int) bool { return bytes.Compare(x.at(i), x.at(j)) < 0 }
func (x *index) Swap(i, j int)      { x.sa[i], x.sa[j] = x.sa[j], x.sa[i] }
func (a *index) at(i int) []byte    { return a.data[a.sa[i]:] }
<|endoftext|>


<commit_before>package acmewrapper

import (
	"crypto/tls"
	"crypto/x509"
	"time"

	"github.com/xenolf/lego/acme"
)

// writeCert takes an acme CertificateResource (as returned from the acme.RenewCertificate
// and the acme.ObtainCertificate functions), and writes the cert and key files from it.
// If the files already exist, it renames the old versions by adding .bak to them. This makes
// sure that a little accident doesn't cause too much damage.
func (w *AcmeWrapper) writeCert(certfile, keyfile string, crt acme.CertificateResource) (err error) {
	if err := w.backupAndSaveFile(certfile, crt.Certificate); err != nil {
		return err
	}
	if err := w.backupAndSaveFile(keyfile, crt.PrivateKey); err != nil {
		return err
	}
	return nil
}

func tlsCert(crt acme.CertificateResource) (*tls.Certificate, error) {
	cert, err := tls.X509KeyPair(crt.Certificate, crt.PrivateKey)
	return &cert, err
}

// CertNeedsUpdate returns whether the current certificate either
// does not exist, or is <X days from expiration, where X is set up in config
func (w *AcmeWrapper) CertNeedsUpdate() bool {
	if w.cert == nil {
		// The cert doesn't exist - it certainly needs update
		return true
	}

	// w.cert.Leaf is not set, so we have to manually parse the certs
	// and make sure that they don't expire soon
	for _, c := range w.cert.Certificate {
		crt, err := x509.ParseCertificate(c)
		if err != nil {
			// If there's an error, we assume the cert is broken, and needs update
			return true
		}
		timeLeft := crt.NotAfter.Sub(time.Now().UTC())
		if timeLeft < w.Config.RenewTime {
			return true
		}
	}

	return false
}
<commit_msg>Only create backups when SaveFileCallback is not provided<commit_after>package acmewrapper

import (
	"crypto/tls"
	"crypto/x509"
	"time"

	"github.com/xenolf/lego/acme"
)

// writeCert takes an acme CertificateResource (as returned from the acme.RenewCertificate
// and the acme.ObtainCertificate functions), and writes the cert and key files from it.
// If the files already exist, it renames the old versions by adding .bak to them. This makes
// sure that a little accident doesn't cause too much damage.
func (w *AcmeWrapper) writeCert(certfile, keyfile string, crt acme.CertificateResource) (err error) {
	//only create a backup version if we're definitely saving to disk
	if w.Config.SaveFileCallback == nil {
		if err := w.backupAndSaveFile(certfile, crt.Certificate); err != nil {
			return err
		}
		if err := w.backupAndSaveFile(keyfile, crt.PrivateKey); err != nil {
			return err
		}
	} else {
		if err := w.saveFile(certfile, crt.Certificate); err != nil {
			return err
		}
		if err := w.saveFile(keyfile, crt.PrivateKey); err != nil {
			return err
		}
	}
	return nil
}

func tlsCert(crt acme.CertificateResource) (*tls.Certificate, error) {
	cert, err := tls.X509KeyPair(crt.Certificate, crt.PrivateKey)
	return &cert, err
}

// CertNeedsUpdate returns whether the current certificate either
// does not exist, or is <X days from expiration, where X is set up in config
func (w *AcmeWrapper) CertNeedsUpdate() bool {
	if w.cert == nil {
		// The cert doesn't exist - it certainly needs update
		return true
	}

	// w.cert.Leaf is not set, so we have to manually parse the certs
	// and make sure that they don't expire soon
	for _, c := range w.cert.Certificate {
		crt, err := x509.ParseCertificate(c)
		if err != nil {
			// If there's an error, we assume the cert is broken, and needs update
			return true
		}
		timeLeft := crt.NotAfter.Sub(time.Now().UTC())
		if timeLeft < w.Config.RenewTime {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io/ioutil"
	"log"
	"net/http"
)

var cmdCert = &Command{
	Run:       Cert,
	UsageLine: "cert -account <keyfile> -domain <domain> [-o <file>] [-p <file>] [-chain]",
	Long: `
Cert generates HTTPS certificates signed by the Let's Encrypt CA.

The -o flag specifies where to write the new certificate
(default is <domain>.crt).

The -p flag specifies where to write the private key for
the new certificate (default is <domain>.key).

If -chain is specified, the resulting certificate includes the
intermediate (issuer) certificate.
`,
}

func init() {
	cmdCert.Flag.StringVar(&accountKeyFile, "account", "", "account key file")
	cmdCert.Flag.StringVar(&certDomain, "domain", "", "domain to issue certificate for")
	cmdCert.Flag.StringVar(&certCertFile, "o", "", "certificate output (default: domain.crt)")
	cmdCert.Flag.StringVar(&certPrivateKeyFile, "p", "", "private key output (default: domain.key)")
	cmdCert.Flag.BoolVar(&certChain, "chain", false, "include intermediate certificate in result")
}

var (
	certDomain         string
	certPrivateKeyFile string
	certCertFile       string
	certChain          bool
)

func Cert(cmd *Command, args []string) {
	if certDomain == "" || accountKeyFile == "" {
		log.Printf("usage error: missing required flags")
		cmd.Usage()
	}
	if certPrivateKeyFile == "" {
		certPrivateKeyFile = certDomain + ".key"
	}
	if certCertFile == "" {
		certCertFile = certDomain + ".crt"
	}

	key, err := ReadKeyFile(accountKeyFile)
	if err != nil {
		log.Fatal(err)
	}

	certKey := NewKeyFile(certPrivateKeyFile, 2048)

	csrTemplate := &x509.CertificateRequest{
		Subject: pkix.Name{
			CommonName: certDomain,
		},
	}
	csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, certKey)
	if err != nil {
		log.Fatalf("error creating certificate request: %s", err)
	}

	resp, body := AcmePost(key, LetsEncryptCA+"/acme/new-cert", map[string]interface{}{
		"resource": "new-cert",
		"csr":      JWSEncoding.EncodeToString(csr),
	})
	if resp.StatusCode != http.StatusCreated {
		log.Fatalf("unexpected response when creating new cert: %s\n%s", resp.Status, formatBody(resp, body))
	}

	// TODO the ACME spec says that the certificate may not be ready in the
	// POST response
	if len(body) == 0 {
		log.Fatalf("error: empty body\ntry to retrieve the certificate from: %s", resp.Header.Get("Location"))
	}

	cert, err := x509.ParseCertificate(body)
	if err != nil {
		log.Fatalf("failed to parse certificate: %s", err)
	}

	issuer := getIssuer()
	verifyCert(cert, issuer)

	var resultCerts []*x509.Certificate
	if certChain {
		resultCerts = []*x509.Certificate{cert, issuer}
	} else {
		resultCerts = []*x509.Certificate{cert}
	}

	buf := new(bytes.Buffer)
	for _, c := range resultCerts {
		err := pem.Encode(buf,
			&pem.Block{
				Type:  "CERTIFICATE",
				Bytes: c.Raw,
			},
		)
		if err != nil {
			log.Fatalf("failed to write PEM block: %s", err)
		}
	}
	if err := ioutil.WriteFile(certCertFile, buf.Bytes(), 0644); err != nil {
		log.Fatalf("error writing certificate file: %s", err)
	}
	log.Printf("created certificate for %s: %s", certDomain, certCertFile)
}

func getIssuer() *x509.Certificate {
	resp, body := AcmeGet(LetsEncryptCA + "/acme/issuer-cert")
	if resp.StatusCode != http.StatusOK || len(body) == 0 {
		log.Fatalf("unable to get issuer certificate: %s", resp.Status)
	}

	issuer, err := x509.ParseCertificate(body)
	if err != nil {
		log.Fatalf("failed to parse issuer certificate: %s", err)
	}
	return issuer
}

func verifyCert(cert, issuer *x509.Certificate) {
	pool := x509.NewCertPool()
	pool.AddCert(issuer)

	_, err := cert.Verify(x509.VerifyOptions{
		DNSName:       certDomain,
		Intermediates: pool,
	})
	if err != nil {
		log.Printf("warning: unable to verify certificate: %s", err)
	}
}
<commit_msg>Better default filenames in cert command<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io/ioutil"
	"log"
	"net/http"
)

var cmdCert = &Command{
	Run:       Cert,
	UsageLine: "cert -account <keyfile> -domain <domain> [-o <file>] [-p <file>] [-chain]",
	Long: `
Cert generates HTTPS certificates signed by the Let's Encrypt CA.

The -o flag specifies where to write the new certificate
(default is <domain>.cert.crt).

The -p flag specifies where to write the private key for
the new certificate (default is <domain>.cert.key).

If -chain is specified, the resulting certificate includes the
intermediate (issuer) certificate.
`,
}

func init() {
	cmdCert.Flag.StringVar(&accountKeyFile, "account", "", "account key file")
	cmdCert.Flag.StringVar(&certDomain, "domain", "", "domain to issue certificate for")
	cmdCert.Flag.StringVar(&certCertFile, "o", "", "certificate output (default: <domain>.cert.crt)")
	cmdCert.Flag.StringVar(&certPrivateKeyFile, "p", "", "private key output (default: <domain>.cert.key)")
	cmdCert.Flag.BoolVar(&certChain, "chain", false, "include intermediate certificate in result")
}

var (
	certDomain         string
	certPrivateKeyFile string
	certCertFile       string
	certChain          bool
)

func Cert(cmd *Command, args []string) {
	if certDomain == "" || accountKeyFile == "" {
		log.Printf("usage error: missing required flags")
		cmd.Usage()
	}
	if certPrivateKeyFile == "" {
		certPrivateKeyFile = certDomain + ".cert.key"
	}
	if certCertFile == "" {
		certCertFile = certDomain + ".cert.crt"
	}

	key, err := ReadKeyFile(accountKeyFile)
	if err != nil {
		log.Fatal(err)
	}

	certKey := NewKeyFile(certPrivateKeyFile, 2048)

	csrTemplate := &x509.CertificateRequest{
		Subject: pkix.Name{
			CommonName: certDomain,
		},
	}
	csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, certKey)
	if err != nil {
		log.Fatalf("error creating certificate request: %s", err)
	}

	resp, body := AcmePost(key, LetsEncryptCA+"/acme/new-cert", map[string]interface{}{
		"resource": "new-cert",
		"csr":      JWSEncoding.EncodeToString(csr),
	})
	if resp.StatusCode != http.StatusCreated {
		log.Fatalf("unexpected response when creating new cert: %s\n%s", resp.Status, formatBody(resp, body))
	}

	// TODO the ACME spec says that the certificate may not be ready in the
	// POST response
	if len(body) == 0 {
		log.Fatalf("error: empty body\ntry to retrieve the certificate from: %s", resp.Header.Get("Location"))
	}

	cert, err := x509.ParseCertificate(body)
	if err != nil {
		log.Fatalf("failed to parse certificate: %s", err)
	}

	issuer := getIssuer()
	verifyCert(cert, issuer)

	var resultCerts []*x509.Certificate
	if certChain {
		resultCerts = []*x509.Certificate{cert, issuer}
	} else {
		resultCerts = []*x509.Certificate{cert}
	}

	buf := new(bytes.Buffer)
	for _, c := range resultCerts {
		err := pem.Encode(buf,
			&pem.Block{
				Type:  "CERTIFICATE",
				Bytes: c.Raw,
			},
		)
		if err != nil {
			log.Fatalf("failed to write PEM block: %s", err)
		}
	}
	if err := ioutil.WriteFile(certCertFile, buf.Bytes(), 0644); err != nil {
		log.Fatalf("error writing certificate file: %s", err)
	}
	log.Printf("created certificate for %s: %s", certDomain, certCertFile)
}

func getIssuer() *x509.Certificate {
	resp, body := AcmeGet(LetsEncryptCA + "/acme/issuer-cert")
	if resp.StatusCode != http.StatusOK || len(body) == 0 {
		log.Fatalf("unable to get issuer certificate: %s", resp.Status)
	}

	issuer, err := x509.ParseCertificate(body)
	if err != nil {
		log.Fatalf("failed to parse issuer certificate: %s", err)
	}
	return issuer
}

func verifyCert(cert, issuer *x509.Certificate) {
	pool := x509.NewCertPool()
	pool.AddCert(issuer)

	_, err := cert.Verify(x509.VerifyOptions{
		DNSName:       certDomain,
		Intermediates: pool,
	})
	if err != nil {
		log.Printf("warning: unable to verify certificate: %s", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"

	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/render"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 300
)

type ChatUI struct {
	Elements []*chatUIElement

	dirty    bool
	Lines    [chatHistoryLines]chat.AnyComponent
	lineFade [chatHistoryLines]float64

	lineLength float64
}

type chatUIElement struct {
	text   *render.UIText
	offset int
	line   int
}

func (c *ChatUI) render(delta float64) {
	for i := range c.lineFade {
		if c.lineFade[i] > 0 {
			c.lineFade[i] -= 0.005 * delta
		} else {
			c.lineFade[i] = 0
		}
	}
	if c.dirty {
		c.dirty = false
		for _, e := range c.Elements {
			if e.text != nil {
				e.text.Free()
			}
		}
		c.Elements = nil

		for i, line := range c.Lines {
			c.newLine()
			if line.Value == nil {
				continue
			}
			c.lineLength = 0
			c.renderComponent(i, line.Value, nil)
		}
	}
	for _, e := range c.Elements {
		e.text.Alpha(c.lineFade[e.line])
	}
}

func (c *ChatUI) renderComponent(line int, co interface{}, color chatGetColorFunc) {
	switch co := co.(type) {
	case *chat.TextComponent:
		getColor := chatGetColor(&co.Component, color)
		width := 0
		runes := []rune(co.Text)
		r, g, b := chatColorRGB(getColor())
		for i := 0; i < len(runes); i++ {
			size := render.SizeOfCharacter(runes[i])
			if width+size > maxLineWidth {
				e := &chatUIElement{
					text:   render.AddUIText(string(runes[:i]), 2+c.lineLength, 480-18, r, g, b),
					offset: 0,
					line:   line,
				}
				c.Elements = append(c.Elements, e)
				c.lineLength = 0
				runes = runes[i:]
				i = 0
				width = 0
				c.newLine()
			}
			width += size
		}
		e := &chatUIElement{
			line:   line,
			text:   render.AddUIText(string(runes), 2+c.lineLength, 480-18, r, g, b),
			offset: 0,
		}
		c.Elements = append(c.Elements, e)
		c.lineLength += e.text.Width
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	default:
		fmt.Printf("Can't handle %T\n", co)
	}
}

type chatGetColorFunc func() chat.Color

func chatGetColor(c *chat.Component, parent chatGetColorFunc) chatGetColorFunc {
	return func() chat.Color {
		if c.Color != "" {
			return c.Color
		}
		if parent != nil {
			return parent()
		}
		return chat.White
	}
}

func chatColorRGB(c chat.Color) (r, g, b int) {
	switch c {
	case chat.Black:
		return 0, 0, 0
	case chat.DarkBlue:
		return 0, 0, 170
	case chat.DarkGreen:
		return 0, 170, 0
	case chat.DarkAqua:
		return 0, 170, 170
	case chat.DarkRed:
		return 170, 0, 0
	case chat.DarkPurple:
		return 170, 0, 170
	case chat.Gold:
		return 255, 170, 0
	case chat.Gray:
		return 170, 170, 170
	case chat.DarkGray:
		return 85, 85, 85
	case chat.Blue:
		return 85, 85, 255
	case chat.Green:
		return 85, 255, 85
	case chat.Aqua:
		return 85, 255, 255
	case chat.Red:
		return 255, 85, 85
	case chat.LightPurple:
		return 255, 85, 255
	case chat.Yellow:
		return 255, 255, 85
	case chat.White:
		return 255, 255, 255

	}
	return 255, 255, 255
}

func (c *ChatUI) newLine() {
	for _, e := range c.Elements {
		if e.text == nil {
			continue
		}
		e.offset++
		if e.offset > 6 {
			e.text.Free()
			e.text = nil
			continue
		}
		e.text.Shift(0, -18)
	}
}

func (c *ChatUI) Add(msg chat.AnyComponent) {
	copy(c.Lines[0:chatHistoryLines-1], c.Lines[1:])
	copy(c.lineFade[0:chatHistoryLines-1], c.lineFade[1:])
	c.Lines[chatHistoryLines-1] = msg
	c.lineFade[chatHistoryLines-1] = 3.0
	c.dirty = true
}
<commit_msg>steven: clean up chat handling<commit_after>// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"

	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/render"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 300
)

type ChatUI struct {
	Elements []*chatUIElement

	dirty    bool
	Lines    [chatHistoryLines]chat.AnyComponent
	lineFade [chatHistoryLines]float64

	lineLength float64
}

type chatUIElement struct {
	text   *render.UIText
	offset int
	line   int
}

func (c *ChatUI) render(delta float64) {
	for i := range c.lineFade {
		if c.lineFade[i] > 0 {
			c.lineFade[i] -= 0.005 * delta
		} else {
			c.lineFade[i] = 0
		}
	}
	if c.dirty {
		c.dirty = false
		for _, e := range c.Elements {
			if e.text != nil {
				e.text.Free()
			}
		}
		c.Elements = nil

		for i, line := range c.Lines {
			c.newLine()
			if line.Value == nil {
				continue
			}
			c.lineLength = 0
			c.renderComponent(i, line.Value, nil)
		}
	}
	for _, e := range c.Elements {
		e.text.Alpha(c.lineFade[e.line])
	}
}

func (c *ChatUI) renderComponent(line int, co interface{}, color chatGetColorFunc) {
	switch co := co.(type) {
	case *chat.TextComponent:
		getColor := chatGetColor(&co.Component, color)
		width := 0
		runes := []rune(co.Text)
		r, g, b := chatColorRGB(getColor())
		for i := 0; i < len(runes); i++ {
			size := render.SizeOfCharacter(runes[i])
			if width+size > maxLineWidth {
				c.appendText(line, string(runes[:i]), r, g, b)
				c.lineLength = 0
				runes = runes[i:]
				i = 0
				width = 0
				c.newLine()
			}
			width += size
		}
		c.lineLength += c.appendText(line, string(runes), r, g, b)
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	default:
		fmt.Printf("Can't handle %T\n", co)
	}
}

func (c *ChatUI) appendText(line int, str string, r, g, b int) float64 {
	e := &chatUIElement{
		text:   render.AddUIText(str, 2+c.lineLength, 480-18, r, g, b),
		offset: 0,
		line:   line,
	}
	c.Elements = append(c.Elements, e)
	return e.text.Width
}

type chatGetColorFunc func() chat.Color

func chatGetColor(c *chat.Component, parent chatGetColorFunc) chatGetColorFunc {
	return func() chat.Color {
		if c.Color != "" {
			return c.Color
		}
		if parent != nil {
			return parent()
		}
		return chat.White
	}
}

func chatColorRGB(c chat.Color) (r, g, b int) {
	switch c {
	case chat.Black:
		return 0, 0, 0
	case chat.DarkBlue:
		return 0, 0, 170
	case chat.DarkGreen:
		return 0, 170, 0
	case chat.DarkAqua:
		return 0, 170, 170
	case chat.DarkRed:
		return 170, 0, 0
	case chat.DarkPurple:
		return 170, 0, 170
	case chat.Gold:
		return 255, 170, 0
	case chat.Gray:
		return 170, 170, 170
	case chat.DarkGray:
		return 85, 85, 85
	case chat.Blue:
		return 85, 85, 255
	case chat.Green:
		return 85, 255, 85
	case chat.Aqua:
		return 85, 255, 255
	case chat.Red:
		return 255, 85, 85
	case chat.LightPurple:
		return 255, 85, 255
	case chat.Yellow:
		return 255, 255, 85
	case chat.White:
		return 255, 255, 255

	}
	return 255, 255, 255
}

func (c *ChatUI) newLine() {
	for _, e := range c.Elements {
		if e.text == nil {
			continue
		}
		e.offset++
		if e.offset > 6 {
			e.text.Free()
			e.text = nil
			continue
		}
		e.text.Shift(0, -18)
	}
}

func (c *ChatUI) Add(msg chat.AnyComponent) {
	copy(c.Lines[0:chatHistoryLines-1], c.Lines[1:])
	copy(c.lineFade[0:chatHistoryLines-1], c.lineFade[1:])
	c.Lines[chatHistoryLines-1] = msg
	c.lineFade[chatHistoryLines-1] = 3.0
	c.dirty = true
}
<|endoftext|>