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

// +build lockdebug

package lock

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"runtime/debug"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sasha-s/go-deadlock"
	"github.com/sirupsen/logrus"
)

const (
	// selfishThresholdSec is the number of seconds that should be used when
	// detecting if a lock was held for more than the specified time.
	selfishThresholdSec = 0.1

	// Waiting for a lock for longer than DeadlockTimeout is considered a deadlock.
	// Ignored is DeadlockTimeout <= 0.
	deadLockTimeout = 310 * time.Second
)

var (
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "lock-lib")

	// selfishThresholdMsg is the message that will be printed when a lock was
	// held for more than selfishThresholdSec.
	selfishThresholdMsg = fmt.Sprintf("Goroutine took lock for more than %.2f seconds", selfishThresholdSec)
)

func init() {
	deadlock.Opts.DeadlockTimeout = deadLockTimeout
}

type internalRWMutex struct {
	deadlock.RWMutex
	t time.Time
}

func (i *internalRWMutex) Lock() {
	i.RWMutex.Lock()
	i.t = time.Now()
}

func (i *internalRWMutex) Unlock() {
	if sec := time.Since(i.t).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) UnlockIgnoreTime() {
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) RLock() {
	i.RWMutex.RLock()
	i.t = time.Now()
}

func (i *internalRWMutex) RUnlock() {
	if sec := time.Since(i.t).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.RUnlock()
}

func (i *internalRWMutex) RUnlockIgnoreTime() {
	i.RWMutex.RUnlock()
}

type internalMutex struct {
	deadlock.Mutex
	time.Time
}

func (i *internalMutex) Lock() {
	i.Mutex.Lock()
	i.Time = time.Now()
}

func (i *internalMutex) Unlock() {
	if sec := time.Since(i.Time).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.Mutex.Unlock()
}

func (i *internalMutex) UnlockIgnoreTime() {
	i.Mutex.Unlock()
}

func printStackTo(sec float64, stack []byte, writer io.Writer) {
	goRoutineNumber := []byte("0")
	newLines := 0

	if bytes.Equal([]byte("goroutine"), stack[:len("goroutine")]) {
		newLines = bytes.Count(stack, []byte{'\n'})
		goroutineLine := bytes.IndexRune(stack, '[')
		goRoutineNumber = stack[:goroutineLine]
	}

	log.WithFields(logrus.Fields{
		"seconds":   sec,
		"goroutine": string(goRoutineNumber[len("goroutine") : len(goRoutineNumber)-1]),
	}).Debug(selfishThresholdMsg)

	// A stack trace is usually in the following format:
	// goroutine 1432 [running]:
	// runtime/debug.Stack(0xc424c4a370, 0xc421f7f750, 0x1)
	//   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
	//   ...
	// To know which trace belongs to which go routine we will append the
	// go routine number to every line of the stack trace.
	writer.Write(bytes.Replace(
		stack,
		[]byte{'\n'},
		append([]byte{'\n'}, goRoutineNumber...),
		// Don't replace the last '\n'
		newLines-1),
	)
}
<commit_msg>pkg/lock: removing tracking time of RLock/RUnlock<commit_after>// Copyright 2017-2019 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.

// +build lockdebug

package lock

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"runtime/debug"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sasha-s/go-deadlock"
	"github.com/sirupsen/logrus"
)

const (
	// selfishThresholdSec is the number of seconds that should be used when
	// detecting if a lock was held for more than the specified time.
	selfishThresholdSec = 0.1

	// Waiting for a lock for longer than DeadlockTimeout is considered a deadlock.
	// Ignored is DeadlockTimeout <= 0.
	deadLockTimeout = 310 * time.Second
)

var (
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "lock-lib")

	// selfishThresholdMsg is the message that will be printed when a lock was
	// held for more than selfishThresholdSec.
	selfishThresholdMsg = fmt.Sprintf("Goroutine took lock for more than %.2f seconds", selfishThresholdSec)
)

func init() {
	deadlock.Opts.DeadlockTimeout = deadLockTimeout
}

type internalRWMutex struct {
	deadlock.RWMutex
	t time.Time
}

func (i *internalRWMutex) Lock() {
	i.RWMutex.Lock()
	i.t = time.Now()
}

func (i *internalRWMutex) Unlock() {
	if sec := time.Since(i.t).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) UnlockIgnoreTime() {
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) RLock() {
	i.RWMutex.RLock()
}

func (i *internalRWMutex) RUnlock() {
	i.RWMutex.RUnlock()
}

func (i *internalRWMutex) RUnlockIgnoreTime() {
	i.RWMutex.RUnlock()
}

type internalMutex struct {
	deadlock.Mutex
	time.Time
}

func (i *internalMutex) Lock() {
	i.Mutex.Lock()
	i.Time = time.Now()
}

func (i *internalMutex) Unlock() {
	if sec := time.Since(i.Time).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.Mutex.Unlock()
}

func (i *internalMutex) UnlockIgnoreTime() {
	i.Mutex.Unlock()
}

func printStackTo(sec float64, stack []byte, writer io.Writer) {
	goRoutineNumber := []byte("0")
	newLines := 0

	if bytes.Equal([]byte("goroutine"), stack[:len("goroutine")]) {
		newLines = bytes.Count(stack, []byte{'\n'})
		goroutineLine := bytes.IndexRune(stack, '[')
		goRoutineNumber = stack[:goroutineLine]
	}

	log.WithFields(logrus.Fields{
		"seconds":   sec,
		"goroutine": string(goRoutineNumber[len("goroutine") : len(goRoutineNumber)-1]),
	}).Debug(selfishThresholdMsg)

	// A stack trace is usually in the following format:
	// goroutine 1432 [running]:
	// runtime/debug.Stack(0xc424c4a370, 0xc421f7f750, 0x1)
	//   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
	//   ...
	// To know which trace belongs to which go routine we will append the
	// go routine number to every line of the stack trace.
	writer.Write(bytes.Replace(
		stack,
		[]byte{'\n'},
		append([]byte{'\n'}, goRoutineNumber...),
		// Don't replace the last '\n'
		newLines-1),
	)
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package natural

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/xconstruct/stark/pkg/util"
	"github.com/xconstruct/stark/proto"
)

const (
	ThisAlgorithmBecomingSkynetCost = 999999999
)

func ParseSimple(text string) (proto.Message, bool) {
	msg := proto.Message{}

	// Raw JSON message
	if strings.HasPrefix(text, "{") {
		if err := json.Unmarshal([]byte(text), &msg); err == nil {
			return msg, true
		}
	}

	if strings.HasPrefix(text, ".") || strings.HasPrefix(text, "!") {
		text = strings.TrimLeft(text, ".! ")
		parts, _ := SplitQuoted(text, " ")
		if parts[0] == "" {
			return msg, false
		}
		msg.Action = parts[0]

		msg.Text = ""
		payload := make(map[string]interface{}, 0)
		for _, part := range parts[1:] {
			keyval, _ := SplitQuoted(part, "=")
			if len(keyval) == 1 {
				if msg.Text != "" {
					msg.Text += " "
				}
				msg.Text += TrimQuotes(keyval[0])
				continue
			}

			k := TrimQuotes(keyval[0])
			v := TrimQuotes(strings.Join(keyval[1:], "="))
			switch k {
			case "text":
				msg.Text = v
			case "device":
				fallthrough
			case "destination":
				msg.Destination = v
			default:
				payload[k] = v
			}
		}
		if len(payload) > 0 {
			msg.EncodePayload(payload)
		}
		return msg, true
	}

	return msg, false
}

func FormatSimple(msg proto.Message) string {
	if msg.Text != "" {
		return msg.Text
	}

	return fmt.Sprintf("%s from %s.", msg.Action, msg.Source)
}

var (
	reTimeIso = regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}[+-]\d{2}\:\d{2})`)
)

func formatTime(s string) string {
	t, err := time.Parse(time.RFC3339, s)
	if err != nil {
		return s
	}

	return util.FuzzyTime(t)
}

func FormatMessage(msg *proto.Message) {
	if msg.Text == "" {
		return
	}

	msg.Text = reTimeIso.ReplaceAllStringFunc(msg.Text, formatTime)
}
<commit_msg>Natural: Support stark:// urls.<commit_after>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package natural

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/xconstruct/stark/pkg/util"
	"github.com/xconstruct/stark/proto"
)

const (
	ThisAlgorithmBecomingSkynetCost = 999999999
)

func ParseSimple(text string) (proto.Message, bool) {
	msg := proto.Message{}

	// Raw JSON message
	if strings.HasPrefix(text, "{") {
		if err := json.Unmarshal([]byte(text), &msg); err == nil {
			return msg, true
		}
	}

	if strings.HasPrefix(text, "stark://") {
		if u, err := url.Parse(text); err == nil {
			if err := msg.EncodePayload(u.Query()); err == nil {
				msg.Action = u.Host + u.Path
				if u.User != nil {
					msg.Destination = u.User.Username()
				}
				return msg, true
			}
		}
	}

	if strings.HasPrefix(text, ".") || strings.HasPrefix(text, "!") {
		text = strings.TrimLeft(text, ".! ")
		parts, _ := SplitQuoted(text, " ")
		if parts[0] == "" {
			return msg, false
		}
		msg.Action = parts[0]

		msg.Text = ""
		payload := make(map[string]interface{}, 0)
		for _, part := range parts[1:] {
			keyval, _ := SplitQuoted(part, "=")
			if len(keyval) == 1 {
				if msg.Text != "" {
					msg.Text += " "
				}
				msg.Text += TrimQuotes(keyval[0])
				continue
			}

			k := TrimQuotes(keyval[0])
			v := TrimQuotes(strings.Join(keyval[1:], "="))
			switch k {
			case "text":
				msg.Text = v
			case "device":
				fallthrough
			case "destination":
				msg.Destination = v
			default:
				payload[k] = v
			}
		}
		if len(payload) > 0 {
			msg.EncodePayload(payload)
		}
		return msg, true
	}

	return msg, false
}

func FormatSimple(msg proto.Message) string {
	if msg.Text != "" {
		return msg.Text
	}

	return fmt.Sprintf("%s from %s.", msg.Action, msg.Source)
}

var (
	reTimeIso = regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}[+-]\d{2}\:\d{2})`)
)

func formatTime(s string) string {
	t, err := time.Parse(time.RFC3339, s)
	if err != nil {
		return s
	}

	return util.FuzzyTime(t)
}

func FormatMessage(msg *proto.Message) {
	if msg.Text == "" {
		return
	}

	msg.Text = reTimeIso.ReplaceAllStringFunc(msg.Text, formatTime)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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 kubelet

import (
	"fmt"
	"io/ioutil"
	"net"
	"path/filepath"

	"github.com/golang/glog"
	cadvisorapiv1 "github.com/google/cadvisor/info/v1"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/kubelet/cm"
	"k8s.io/kubernetes/pkg/kubelet/config"
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
	utilfile "k8s.io/kubernetes/pkg/util/file"
	utilnode "k8s.io/kubernetes/pkg/util/node"
	volumeutil "k8s.io/kubernetes/pkg/volume/util"
)

// getRootDir returns the full path to the directory under which kubelet can
// store data.  These functions are useful to pass interfaces to other modules
// that may need to know where to write data without getting a whole kubelet
// instance.
func (kl *Kubelet) getRootDir() string {
	return kl.rootDirectory
}

// getPodsDir returns the full path to the directory under which pod
// directories are created.
func (kl *Kubelet) getPodsDir() string {
	return filepath.Join(kl.getRootDir(), config.DefaultKubeletPodsDirName)
}

// getPluginsDir returns the full path to the directory under which plugin
// directories are created.  Plugins can use these directories for data that
// they need to persist.  Plugins should create subdirectories under this named
// after their own names.
func (kl *Kubelet) getPluginsDir() string {
	return filepath.Join(kl.getRootDir(), config.DefaultKubeletPluginsDirName)
}

// getPluginDir returns a data directory name for a given plugin name.
// Plugins can use these directories to store data that they need to persist.
// For per-pod plugin data, see getPodPluginDir.
func (kl *Kubelet) getPluginDir(pluginName string) string {
	return filepath.Join(kl.getPluginsDir(), pluginName)
}

// getVolumeDevicePluginsDir returns the full path to the directory under which plugin
// directories are created.  Plugins can use these directories for data that
// they need to persist.  Plugins should create subdirectories under this named
// after their own names.
func (kl *Kubelet) getVolumeDevicePluginsDir() string {
	return filepath.Join(kl.getRootDir(), config.DefaultKubeletPluginsDirName)
}

// getVolumeDevicePluginDir returns a data directory name for a given plugin name.
// Plugins can use these directories to store data that they need to persist.
// For per-pod plugin data, see getVolumeDevicePluginsDir.
func (kl *Kubelet) getVolumeDevicePluginDir(pluginName string) string {
	return filepath.Join(kl.getVolumeDevicePluginsDir(), pluginName, config.DefaultKubeletVolumeDevicesDirName)
}

// GetPodDir returns the full path to the per-pod data directory for the
// specified pod. This directory may not exist if the pod does not exist.
func (kl *Kubelet) GetPodDir(podUID types.UID) string {
	return kl.getPodDir(podUID)
}

// getPodDir returns the full path to the per-pod directory for the pod with
// the given UID.
func (kl *Kubelet) getPodDir(podUID types.UID) string {
	return filepath.Join(kl.getPodsDir(), string(podUID))
}

// getPodVolumesDir returns the full path to the per-pod data directory under
// which volumes are created for the specified pod.  This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodVolumesDir(podUID types.UID) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletVolumesDirName)
}

// getPodVolumeDir returns the full path to the directory which represents the
// named volume under the named plugin for specified pod.  This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
	return filepath.Join(kl.getPodVolumesDir(podUID), pluginName, volumeName)
}

// getPodVolumeDevicesDir returns the full path to the per-pod data directory under
// which volumes are created for the specified pod. This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodVolumeDevicesDir(podUID types.UID) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletVolumeDevicesDirName)
}

// getPodVolumeDeviceDir returns the full path to the directory which represents the
// named plugin for specified pod. This directory may not exist if the pod does not exist.
func (kl *Kubelet) getPodVolumeDeviceDir(podUID types.UID, pluginName string) string {
	return filepath.Join(kl.getPodVolumeDevicesDir(podUID), pluginName)
}

// getPodPluginsDir returns the full path to the per-pod data directory under
// which plugins may store data for the specified pod.  This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodPluginsDir(podUID types.UID) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletPluginsDirName)
}

// getPodPluginDir returns a data directory name for a given plugin name for a
// given pod UID.  Plugins can use these directories to store data that they
// need to persist.  For non-per-pod plugin data, see getPluginDir.
func (kl *Kubelet) getPodPluginDir(podUID types.UID, pluginName string) string {
	return filepath.Join(kl.getPodPluginsDir(podUID), pluginName)
}

// getPodContainerDir returns the full path to the per-pod data directory under
// which container data is held for the specified pod.  This directory may not
// exist if the pod or container does not exist.
func (kl *Kubelet) getPodContainerDir(podUID types.UID, ctrName string) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletContainersDirName, ctrName)
}

// GetPods returns all pods bound to the kubelet and their spec, and the mirror
// pods.
func (kl *Kubelet) GetPods() []*v1.Pod {
	pods := kl.podManager.GetPods()
	// a kubelet running without apiserver requires an additional
	// update of the static pod status. See #57106
	for _, p := range pods {
		if status, ok := kl.statusManager.GetPodStatus(p.UID); ok {
			p.Status = status
		}
	}
	return pods
}

// GetRunningPods returns all pods running on kubelet from looking at the
// container runtime cache. This function converts kubecontainer.Pod to
// v1.Pod, so only the fields that exist in both kubecontainer.Pod and
// v1.Pod are considered meaningful.
func (kl *Kubelet) GetRunningPods() ([]*v1.Pod, error) {
	pods, err := kl.runtimeCache.GetPods()
	if err != nil {
		return nil, err
	}

	apiPods := make([]*v1.Pod, 0, len(pods))
	for _, pod := range pods {
		apiPods = append(apiPods, pod.ToAPIPod())
	}
	return apiPods, nil
}

// GetPodByFullName gets the pod with the given 'full' name, which
// incorporates the namespace as well as whether the pod was found.
func (kl *Kubelet) GetPodByFullName(podFullName string) (*v1.Pod, bool) {
	return kl.podManager.GetPodByFullName(podFullName)
}

// GetPodByName provides the first pod that matches namespace and name, as well
// as whether the pod was found.
func (kl *Kubelet) GetPodByName(namespace, name string) (*v1.Pod, bool) {
	return kl.podManager.GetPodByName(namespace, name)
}

// GetHostname Returns the hostname as the kubelet sees it.
func (kl *Kubelet) GetHostname() string {
	return kl.hostname
}

// getRuntime returns the current Runtime implementation in use by the kubelet.
func (kl *Kubelet) getRuntime() kubecontainer.Runtime {
	return kl.containerRuntime
}

// GetNode returns the node info for the configured node name of this Kubelet.
func (kl *Kubelet) GetNode() (*v1.Node, error) {
	if kl.kubeClient == nil {
		return kl.initialNode()
	}
	return kl.nodeInfo.GetNodeInfo(string(kl.nodeName))
}

// getNodeAnyWay() must return a *v1.Node which is required by RunGeneralPredicates().
// The *v1.Node is obtained as follows:
// Return kubelet's nodeInfo for this node, except on error or if in standalone mode,
// in which case return a manufactured nodeInfo representing a node with no pods,
// zero capacity, and the default labels.
func (kl *Kubelet) getNodeAnyWay() (*v1.Node, error) {
	if kl.kubeClient != nil {
		if n, err := kl.nodeInfo.GetNodeInfo(string(kl.nodeName)); err == nil {
			return n, nil
		}
	}
	return kl.initialNode()
}

// GetNodeConfig returns the container manager node config.
func (kl *Kubelet) GetNodeConfig() cm.NodeConfig {
	return kl.containerManager.GetNodeConfig()
}

// GetHostIP returns host IP or nil in case of error.
func (kl *Kubelet) GetHostIP() (net.IP, error) {
	node, err := kl.GetNode()
	if err != nil {
		return nil, fmt.Errorf("cannot get node: %v", err)
	}
	return utilnode.GetNodeHostIP(node)
}

// getHostIPAnyway attempts to return the host IP from kubelet's nodeInfo, or
// the initialNode.
func (kl *Kubelet) getHostIPAnyWay() (net.IP, error) {
	node, err := kl.getNodeAnyWay()
	if err != nil {
		return nil, err
	}
	return utilnode.GetNodeHostIP(node)
}

// GetExtraSupplementalGroupsForPod returns a list of the extra
// supplemental groups for the Pod. These extra supplemental groups come
// from annotations on persistent volumes that the pod depends on.
func (kl *Kubelet) GetExtraSupplementalGroupsForPod(pod *v1.Pod) []int64 {
	return kl.volumeManager.GetExtraSupplementalGroupsForPod(pod)
}

// getPodVolumePathListFromDisk returns a list of the volume paths by reading the
// volume directories for the given pod from the disk.
func (kl *Kubelet) getPodVolumePathListFromDisk(podUID types.UID) ([]string, error) {
	volumes := []string{}
	podVolDir := kl.getPodVolumesDir(podUID)

	if pathExists, pathErr := volumeutil.PathExists(podVolDir); pathErr != nil {
		return volumes, fmt.Errorf("Error checking if path %q exists: %v", podVolDir, pathErr)
	} else if !pathExists {
		glog.Warningf("Path %q does not exist", podVolDir)
		return volumes, nil
	}

	volumePluginDirs, err := ioutil.ReadDir(podVolDir)
	if err != nil {
		glog.Errorf("Could not read directory %s: %v", podVolDir, err)
		return volumes, err
	}
	for _, volumePluginDir := range volumePluginDirs {
		volumePluginName := volumePluginDir.Name()
		volumePluginPath := filepath.Join(podVolDir, volumePluginName)
		volumeDirs, err := utilfile.ReadDirNoStat(volumePluginPath)
		if err != nil {
			return volumes, fmt.Errorf("Could not read directory %s: %v", volumePluginPath, err)
		}
		for _, volumeDir := range volumeDirs {
			volumes = append(volumes, filepath.Join(volumePluginPath, volumeDir))
		}
	}
	return volumes, nil
}

// GetVersionInfo returns information about the version of cAdvisor in use.
func (kl *Kubelet) GetVersionInfo() (*cadvisorapiv1.VersionInfo, error) {
	return kl.cadvisor.VersionInfo()
}

// GetCachedMachineInfo assumes that the machine info can't change without a reboot
func (kl *Kubelet) GetCachedMachineInfo() (*cadvisorapiv1.MachineInfo, error) {
	if kl.machineInfo == nil {
		info, err := kl.cadvisor.MachineInfo()
		if err != nil {
			return nil, err
		}
		kl.machineInfo = info
	}
	return kl.machineInfo, nil
}
<commit_msg>kubelet: revert the get pod status<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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 kubelet

import (
	"fmt"
	"io/ioutil"
	"net"
	"path/filepath"

	"github.com/golang/glog"
	cadvisorapiv1 "github.com/google/cadvisor/info/v1"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/kubelet/cm"
	"k8s.io/kubernetes/pkg/kubelet/config"
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
	utilfile "k8s.io/kubernetes/pkg/util/file"
	utilnode "k8s.io/kubernetes/pkg/util/node"
	volumeutil "k8s.io/kubernetes/pkg/volume/util"
)

// getRootDir returns the full path to the directory under which kubelet can
// store data.  These functions are useful to pass interfaces to other modules
// that may need to know where to write data without getting a whole kubelet
// instance.
func (kl *Kubelet) getRootDir() string {
	return kl.rootDirectory
}

// getPodsDir returns the full path to the directory under which pod
// directories are created.
func (kl *Kubelet) getPodsDir() string {
	return filepath.Join(kl.getRootDir(), config.DefaultKubeletPodsDirName)
}

// getPluginsDir returns the full path to the directory under which plugin
// directories are created.  Plugins can use these directories for data that
// they need to persist.  Plugins should create subdirectories under this named
// after their own names.
func (kl *Kubelet) getPluginsDir() string {
	return filepath.Join(kl.getRootDir(), config.DefaultKubeletPluginsDirName)
}

// getPluginDir returns a data directory name for a given plugin name.
// Plugins can use these directories to store data that they need to persist.
// For per-pod plugin data, see getPodPluginDir.
func (kl *Kubelet) getPluginDir(pluginName string) string {
	return filepath.Join(kl.getPluginsDir(), pluginName)
}

// getVolumeDevicePluginsDir returns the full path to the directory under which plugin
// directories are created.  Plugins can use these directories for data that
// they need to persist.  Plugins should create subdirectories under this named
// after their own names.
func (kl *Kubelet) getVolumeDevicePluginsDir() string {
	return filepath.Join(kl.getRootDir(), config.DefaultKubeletPluginsDirName)
}

// getVolumeDevicePluginDir returns a data directory name for a given plugin name.
// Plugins can use these directories to store data that they need to persist.
// For per-pod plugin data, see getVolumeDevicePluginsDir.
func (kl *Kubelet) getVolumeDevicePluginDir(pluginName string) string {
	return filepath.Join(kl.getVolumeDevicePluginsDir(), pluginName, config.DefaultKubeletVolumeDevicesDirName)
}

// GetPodDir returns the full path to the per-pod data directory for the
// specified pod. This directory may not exist if the pod does not exist.
func (kl *Kubelet) GetPodDir(podUID types.UID) string {
	return kl.getPodDir(podUID)
}

// getPodDir returns the full path to the per-pod directory for the pod with
// the given UID.
func (kl *Kubelet) getPodDir(podUID types.UID) string {
	return filepath.Join(kl.getPodsDir(), string(podUID))
}

// getPodVolumesDir returns the full path to the per-pod data directory under
// which volumes are created for the specified pod.  This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodVolumesDir(podUID types.UID) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletVolumesDirName)
}

// getPodVolumeDir returns the full path to the directory which represents the
// named volume under the named plugin for specified pod.  This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
	return filepath.Join(kl.getPodVolumesDir(podUID), pluginName, volumeName)
}

// getPodVolumeDevicesDir returns the full path to the per-pod data directory under
// which volumes are created for the specified pod. This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodVolumeDevicesDir(podUID types.UID) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletVolumeDevicesDirName)
}

// getPodVolumeDeviceDir returns the full path to the directory which represents the
// named plugin for specified pod. This directory may not exist if the pod does not exist.
func (kl *Kubelet) getPodVolumeDeviceDir(podUID types.UID, pluginName string) string {
	return filepath.Join(kl.getPodVolumeDevicesDir(podUID), pluginName)
}

// getPodPluginsDir returns the full path to the per-pod data directory under
// which plugins may store data for the specified pod.  This directory may not
// exist if the pod does not exist.
func (kl *Kubelet) getPodPluginsDir(podUID types.UID) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletPluginsDirName)
}

// getPodPluginDir returns a data directory name for a given plugin name for a
// given pod UID.  Plugins can use these directories to store data that they
// need to persist.  For non-per-pod plugin data, see getPluginDir.
func (kl *Kubelet) getPodPluginDir(podUID types.UID, pluginName string) string {
	return filepath.Join(kl.getPodPluginsDir(podUID), pluginName)
}

// getPodContainerDir returns the full path to the per-pod data directory under
// which container data is held for the specified pod.  This directory may not
// exist if the pod or container does not exist.
func (kl *Kubelet) getPodContainerDir(podUID types.UID, ctrName string) string {
	return filepath.Join(kl.getPodDir(podUID), config.DefaultKubeletContainersDirName, ctrName)
}

// GetPods returns all pods bound to the kubelet and their spec, and the mirror
// pods.
func (kl *Kubelet) GetPods() []*v1.Pod {
	return kl.podManager.GetPods()
}

// GetRunningPods returns all pods running on kubelet from looking at the
// container runtime cache. This function converts kubecontainer.Pod to
// v1.Pod, so only the fields that exist in both kubecontainer.Pod and
// v1.Pod are considered meaningful.
func (kl *Kubelet) GetRunningPods() ([]*v1.Pod, error) {
	pods, err := kl.runtimeCache.GetPods()
	if err != nil {
		return nil, err
	}

	apiPods := make([]*v1.Pod, 0, len(pods))
	for _, pod := range pods {
		apiPods = append(apiPods, pod.ToAPIPod())
	}
	return apiPods, nil
}

// GetPodByFullName gets the pod with the given 'full' name, which
// incorporates the namespace as well as whether the pod was found.
func (kl *Kubelet) GetPodByFullName(podFullName string) (*v1.Pod, bool) {
	return kl.podManager.GetPodByFullName(podFullName)
}

// GetPodByName provides the first pod that matches namespace and name, as well
// as whether the pod was found.
func (kl *Kubelet) GetPodByName(namespace, name string) (*v1.Pod, bool) {
	return kl.podManager.GetPodByName(namespace, name)
}

// GetHostname Returns the hostname as the kubelet sees it.
func (kl *Kubelet) GetHostname() string {
	return kl.hostname
}

// getRuntime returns the current Runtime implementation in use by the kubelet.
func (kl *Kubelet) getRuntime() kubecontainer.Runtime {
	return kl.containerRuntime
}

// GetNode returns the node info for the configured node name of this Kubelet.
func (kl *Kubelet) GetNode() (*v1.Node, error) {
	if kl.kubeClient == nil {
		return kl.initialNode()
	}
	return kl.nodeInfo.GetNodeInfo(string(kl.nodeName))
}

// getNodeAnyWay() must return a *v1.Node which is required by RunGeneralPredicates().
// The *v1.Node is obtained as follows:
// Return kubelet's nodeInfo for this node, except on error or if in standalone mode,
// in which case return a manufactured nodeInfo representing a node with no pods,
// zero capacity, and the default labels.
func (kl *Kubelet) getNodeAnyWay() (*v1.Node, error) {
	if kl.kubeClient != nil {
		if n, err := kl.nodeInfo.GetNodeInfo(string(kl.nodeName)); err == nil {
			return n, nil
		}
	}
	return kl.initialNode()
}

// GetNodeConfig returns the container manager node config.
func (kl *Kubelet) GetNodeConfig() cm.NodeConfig {
	return kl.containerManager.GetNodeConfig()
}

// GetHostIP returns host IP or nil in case of error.
func (kl *Kubelet) GetHostIP() (net.IP, error) {
	node, err := kl.GetNode()
	if err != nil {
		return nil, fmt.Errorf("cannot get node: %v", err)
	}
	return utilnode.GetNodeHostIP(node)
}

// getHostIPAnyway attempts to return the host IP from kubelet's nodeInfo, or
// the initialNode.
func (kl *Kubelet) getHostIPAnyWay() (net.IP, error) {
	node, err := kl.getNodeAnyWay()
	if err != nil {
		return nil, err
	}
	return utilnode.GetNodeHostIP(node)
}

// GetExtraSupplementalGroupsForPod returns a list of the extra
// supplemental groups for the Pod. These extra supplemental groups come
// from annotations on persistent volumes that the pod depends on.
func (kl *Kubelet) GetExtraSupplementalGroupsForPod(pod *v1.Pod) []int64 {
	return kl.volumeManager.GetExtraSupplementalGroupsForPod(pod)
}

// getPodVolumePathListFromDisk returns a list of the volume paths by reading the
// volume directories for the given pod from the disk.
func (kl *Kubelet) getPodVolumePathListFromDisk(podUID types.UID) ([]string, error) {
	volumes := []string{}
	podVolDir := kl.getPodVolumesDir(podUID)

	if pathExists, pathErr := volumeutil.PathExists(podVolDir); pathErr != nil {
		return volumes, fmt.Errorf("Error checking if path %q exists: %v", podVolDir, pathErr)
	} else if !pathExists {
		glog.Warningf("Path %q does not exist", podVolDir)
		return volumes, nil
	}

	volumePluginDirs, err := ioutil.ReadDir(podVolDir)
	if err != nil {
		glog.Errorf("Could not read directory %s: %v", podVolDir, err)
		return volumes, err
	}
	for _, volumePluginDir := range volumePluginDirs {
		volumePluginName := volumePluginDir.Name()
		volumePluginPath := filepath.Join(podVolDir, volumePluginName)
		volumeDirs, err := utilfile.ReadDirNoStat(volumePluginPath)
		if err != nil {
			return volumes, fmt.Errorf("Could not read directory %s: %v", volumePluginPath, err)
		}
		for _, volumeDir := range volumeDirs {
			volumes = append(volumes, filepath.Join(volumePluginPath, volumeDir))
		}
	}
	return volumes, nil
}

// GetVersionInfo returns information about the version of cAdvisor in use.
func (kl *Kubelet) GetVersionInfo() (*cadvisorapiv1.VersionInfo, error) {
	return kl.cadvisor.VersionInfo()
}

// GetCachedMachineInfo assumes that the machine info can't change without a reboot
func (kl *Kubelet) GetCachedMachineInfo() (*cadvisorapiv1.MachineInfo, error) {
	if kl.machineInfo == nil {
		info, err := kl.cadvisor.MachineInfo()
		if err != nil {
			return nil, err
		}
		kl.machineInfo = info
	}
	return kl.machineInfo, nil
}
<|endoftext|>


<commit_before>package options

import (
	"strconv"

	"github.com/libopenstorage/openstorage/api"
)

// Options specifies keys from a key-value pair
// that can be passed in to the APIS
const (
	// OptionsSecret is an option provided to the following Openstorage Volume API
	// - Attach
	// It indicates the name of the secret stored in a secret store
	// SECRET_NAME in case of hashicorp's Vault will be the key from the key-value pair stored in its kv backend.
	// SECRET_NAME for Kubernetes secret, it is the name of the secret object itself
	OptionsSecret = "SECRET_NAME"
	// OptionsSecretKey is an option provided to the following Openstorage Volume API
	// - Attach
	// SECRET_KEY in case of kubernetes will be the key stored in the kubernetes secret with name SECRET_NAME
	OptionsSecretKey = "SECRET_KEY"
	// OptionsSecretContext is an option provided to the following Openstorage Volume API
	// - Attach
	// It indicates the additional context which could be used to retrieve the secret
	// SECRET_CONTEXT in case of kubernetes secret is the namespace in which the secret is created
	OptionsSecretContext = "SECRET_CONTEXT"
	// OptionsUnmountBeforeDetach is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver to issue an Unmount before trying the detach operation
	OptionsUnmountBeforeDetach = "UNMOUNT_BEFORE_DETACH"
	// OptionsDeleteAfterUnmount is an option provided to the following Openstorage Volume API
	// - Unmount
	// It indicates the Volume Driver to delete the mount path after a successful Unmount
	OptionsDeleteAfterUnmount = "DELETE_AFTER_UNMOUNT"
	// OptionsWaitBeforeDelete is an option provided to the following Openstorage Volume API
	// - Unmount
	// This option is used in conjunction with OptionsDeleteAfterUnmount.
	// It indicates the Volume Driver to introduce a delay before deleting mount path
	OptionsWaitBeforeDelete = "WAIT_BEFORE_DELETE"
	// OptionsRedirectDetach is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver to redirect detach to the node where volume is attached
	OptionsRedirectDetach = "REDIRECT_DETACH"
	// OptionsDeviceFuseMount is an option provided to the following Openstorage Volume APIs
	// - Mount
	// - Unmount
	// It is used for volume types which use FUSE mounts.
	// It provides the Volume Driver with the underlying name of fuse mount device
	OptionsDeviceFuseMount = "DEV_FUSE_MOUNT"
	// OptionsForceDetach is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver to forcefully detach device from kernel
	OptionsForceDetach = "FORCE_DETACH"
	// OptionsAccessMode is an option provided to the following Openstorage Volume API
	// - Mount
	// It indicates the mode in which volume must be mounted
	OptionsAccessMode = "ACCESS_MODE"
	// OptionsFastpath is an option to control IO path
	// - Attach
	// It indicates which IO path to use to complete user IO
	OptionsFastpath = "FASTPATH_STATE"
)

// IsBoolOptionSet checks if a boolean option key is set
func IsBoolOptionSet(options map[string]string, key string) bool {
	if options != nil {
		if value, ok := options[key]; ok {
			if b, err := strconv.ParseBool(value); err == nil {
				return b
			}
		}
	}

	return false
}

// NewVolumeAttachOptions converts a map of options to api.SdkVolumeAttachOptions
func NewVolumeAttachOptions(options map[string]string) *api.SdkVolumeAttachOptions {
	return &api.SdkVolumeAttachOptions{
		SecretName:    options[OptionsSecret],
		SecretKey:     options[OptionsSecretKey],
		SecretContext: options[OptionsSecretContext],
		Fastpath:      options[OptionsFastpath],
	}
}

// NewVolumeUnmountOptions converts a map of options to api.SdkVolumeUnmounOptions
func NewVolumeUnmountOptions(options map[string]string) *api.SdkVolumeUnmountOptions {
	return &api.SdkVolumeUnmountOptions{
		DeleteMountPath:                IsBoolOptionSet(options, OptionsDeleteAfterUnmount),
		NoDelayBeforeDeletingMountPath: IsBoolOptionSet(options, OptionsWaitBeforeDelete),
	}
}
<commit_msg>Add an option to indicate if a Detach was issued as a part of Delete (#1908)<commit_after>package options

import (
	"strconv"

	"github.com/libopenstorage/openstorage/api"
)

// Options specifies keys from a key-value pair
// that can be passed in to the APIS
const (
	// OptionsSecret is an option provided to the following Openstorage Volume API
	// - Attach
	// It indicates the name of the secret stored in a secret store
	// SECRET_NAME in case of hashicorp's Vault will be the key from the key-value pair stored in its kv backend.
	// SECRET_NAME for Kubernetes secret, it is the name of the secret object itself
	OptionsSecret = "SECRET_NAME"
	// OptionsSecretKey is an option provided to the following Openstorage Volume API
	// - Attach
	// SECRET_KEY in case of kubernetes will be the key stored in the kubernetes secret with name SECRET_NAME
	OptionsSecretKey = "SECRET_KEY"
	// OptionsSecretContext is an option provided to the following Openstorage Volume API
	// - Attach
	// It indicates the additional context which could be used to retrieve the secret
	// SECRET_CONTEXT in case of kubernetes secret is the namespace in which the secret is created
	OptionsSecretContext = "SECRET_CONTEXT"
	// OptionsUnmountBeforeDetach is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver to issue an Unmount before trying the detach operation
	OptionsUnmountBeforeDetach = "UNMOUNT_BEFORE_DETACH"
	// OptionsDeleteAfterUnmount is an option provided to the following Openstorage Volume API
	// - Unmount
	// It indicates the Volume Driver to delete the mount path after a successful Unmount
	OptionsDeleteAfterUnmount = "DELETE_AFTER_UNMOUNT"
	// OptionsWaitBeforeDelete is an option provided to the following Openstorage Volume API
	// - Unmount
	// This option is used in conjunction with OptionsDeleteAfterUnmount.
	// It indicates the Volume Driver to introduce a delay before deleting mount path
	OptionsWaitBeforeDelete = "WAIT_BEFORE_DELETE"
	// OptionsRedirectDetach is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver to redirect detach to the node where volume is attached
	OptionsRedirectDetach = "REDIRECT_DETACH"
	// OptionsDetachDuringDelete is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver that a Detach is being issued as a part of a Delete request
	OptionsDetachDuringDelete = "DETACH_DURING_DELETE"
	// OptionsDeviceFuseMount is an option provided to the following Openstorage Volume APIs
	// - Mount
	// - Unmount
	// It is used for volume types which use FUSE mounts.
	// It provides the Volume Driver with the underlying name of fuse mount device
	OptionsDeviceFuseMount = "DEV_FUSE_MOUNT"
	// OptionsForceDetach is an option provided to the following Openstorage Volume API
	// - Detach
	// It indicates the Volume Driver to forcefully detach device from kernel
	OptionsForceDetach = "FORCE_DETACH"
	// OptionsAccessMode is an option provided to the following Openstorage Volume API
	// - Mount
	// It indicates the mode in which volume must be mounted
	OptionsAccessMode = "ACCESS_MODE"
	// OptionsFastpath is an option to control IO path
	// - Attach
	// It indicates which IO path to use to complete user IO
	OptionsFastpath = "FASTPATH_STATE"
)

// IsBoolOptionSet checks if a boolean option key is set
func IsBoolOptionSet(options map[string]string, key string) bool {
	if options != nil {
		if value, ok := options[key]; ok {
			if b, err := strconv.ParseBool(value); err == nil {
				return b
			}
		}
	}

	return false
}

// NewVolumeAttachOptions converts a map of options to api.SdkVolumeAttachOptions
func NewVolumeAttachOptions(options map[string]string) *api.SdkVolumeAttachOptions {
	return &api.SdkVolumeAttachOptions{
		SecretName:    options[OptionsSecret],
		SecretKey:     options[OptionsSecretKey],
		SecretContext: options[OptionsSecretContext],
		Fastpath:      options[OptionsFastpath],
	}
}

// NewVolumeUnmountOptions converts a map of options to api.SdkVolumeUnmounOptions
func NewVolumeUnmountOptions(options map[string]string) *api.SdkVolumeUnmountOptions {
	return &api.SdkVolumeUnmountOptions{
		DeleteMountPath:                IsBoolOptionSet(options, OptionsDeleteAfterUnmount),
		NoDelayBeforeDeletingMountPath: IsBoolOptionSet(options, OptionsWaitBeforeDelete),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
	"time"

	mkr "github.com/mackerelio/mackerel-client-go"
)

func TestFormatJoinedAlert(t *testing.T) {

	const location = "UTC"

	loc, err := time.LoadLocation(location)
	if err != nil {
		loc = time.FixedZone(location, 0*60*60)
	}
	time.Local = loc

	testCases := []struct {
		alertSet *alertSet
		want     string
	}{
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "connectivity", Status: "CRITICAL", HostID: "3XYyG", MonitorID: "5rXR3", OpenedAt: 100},
				&mkr.Host{ID: "3XYyG", Name: "app.example.com", Roles: mkr.Roles{"foo": {"bar", "baz"}}, Status: "working"},
				&mkr.MonitorConnectivity{ID: "5rXR3", Type: "connectivity", Name: "connectivity"},
			},
			"2tZhm 1970-01-01 00:01:40 CRITICAL connectivity app.example.com working [foo:bar,baz]",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "host", Status: "CRITICAL", HostID: "3XYyG", MonitorID: "5rXR3", Value: 15.7, OpenedAt: 200},
				&mkr.Host{ID: "3XYyG", Name: "app.example.com", Roles: mkr.Roles{"foo": {"bar", "baz"}}, Status: "working"},
				&mkr.MonitorHostMetric{ID: "5rXR3", Type: "host", Name: "All::loadavg5", Metric: "loadavg5", Warning: 8.0, Critical: 12.0, Operator: ">"},
			},
			"2tZhm 1970-01-01 00:03:20 CRITICAL All::loadavg5 loadavg5 15.70 > 12.00 app.example.com working [foo:bar,baz]",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "service", Status: "WARNING", MonitorID: "5rXR3", Value: 15.7, OpenedAt: 300},
				nil,
				&mkr.MonitorServiceMetric{ID: "5rXR3", Type: "service", Service: "ServiceFoo", Name: "bar.baz monitor", Metric: "custom.bar.baz", Warning: 10.0, Critical: 20.0, Operator: ">"},
			},
			"2tZhm 1970-01-01 00:05:00 WARNING bar.baz monitor ServiceFoo custom.bar.baz 15.70 > 10.00",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "external", Status: "CRITICAL", MonitorID: "5rXR3", Value: 2500, Message: "200", OpenedAt: 400},
				nil,
				&mkr.MonitorExternalHTTP{ID: "5rXR3", Type: "external", Name: "Example Domain", URL: "https://example.com", ResponseTimeWarning: 500, ResponseTimeCritical: 1000, ResponseTimeDuration: 5},
			},
			"2tZhm 1970-01-01 00:06:40 CRITICAL Example Domain https://example.com 2500.00 > 1000.00 msec, status:200",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "expression", Status: "WARNING", MonitorID: "5rXR3", Value: 15.7, OpenedAt: 500},
				nil,
				&mkr.MonitorExpression{ID: "5rXR3", Type: "expression", Name: "Max loadavg5 monitor", Expression: "max(\n  roleSlots(\n    'service:role',\n    'loadavg5'\n  )\n)\n", Warning: 10.0, Critical: 20.0, Operator: ">"},
			},
			"2tZhm 1970-01-01 00:08:20 WARNING Max loadavg5 monitor max(roleSlots('service:role', 'loadavg5')) 15.70 > 10.00",
		},
	}

	for _, testCase := range testCases {
		str := formatJoinedAlert(testCase.alertSet, false)
		if str != testCase.want {
			t.Errorf("should be '%s' but got '%s'", testCase.want, str)
		}
	}
}
<commit_msg>tweak alerts test<commit_after>package main

import (
	"testing"
	"time"

	mkr "github.com/mackerelio/mackerel-client-go"
)

func TestFormatJoinedAlert(t *testing.T) {

	const location = "UTC"

	loc, err := time.LoadLocation(location)
	if err != nil {
		loc = time.FixedZone(location, 0*60*60)
	}
	time.Local = loc

	testCases := []struct {
		alertSet *alertSet
		want     string
	}{
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "connectivity", Status: "CRITICAL", HostID: "3XYyG", MonitorID: "5rXR3", OpenedAt: 100},
				&mkr.Host{ID: "3XYyG", Name: "app.example.com", Roles: mkr.Roles{"foo": {"bar", "baz"}}, Status: "working"},
				&mkr.MonitorConnectivity{ID: "5rXR3", Type: "connectivity", Name: "connectivity"},
			},
			"2tZhm 1970-01-01 00:01:40 CRITICAL connectivity app.example.com working [foo:bar,baz]",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "host", Status: "CRITICAL", HostID: "3XYyG", MonitorID: "5rXR3", Value: 15.7, OpenedAt: 200},
				&mkr.Host{ID: "3XYyG", Name: "app.example.com", Roles: mkr.Roles{"foo": {"bar", "baz"}}, Status: "working"},
				&mkr.MonitorHostMetric{ID: "5rXR3", Type: "host", Name: "All::loadavg5", Metric: "loadavg5", Warning: 8.0, Critical: 12.0, Operator: ">"},
			},
			"2tZhm 1970-01-01 00:03:20 CRITICAL All::loadavg5 loadavg5 15.70 > 12.00 app.example.com working [foo:bar,baz]",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "service", Status: "WARNING", MonitorID: "5rXR3", Value: 15.7, OpenedAt: 300},
				nil,
				&mkr.MonitorServiceMetric{ID: "5rXR3", Type: "service", Service: "ServiceFoo", Name: "bar.baz monitor", Metric: "custom.bar.baz", Warning: 10.0, Critical: 20.0, Operator: ">"},
			},
			"2tZhm 1970-01-01 00:05:00 WARNING bar.baz monitor ServiceFoo custom.bar.baz 15.70 > 10.00",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "external", Status: "CRITICAL", MonitorID: "5rXR3", Value: 2500, Message: "200", OpenedAt: 400},
				nil,
				&mkr.MonitorExternalHTTP{ID: "5rXR3", Type: "external", Name: "Example Domain", URL: "https://example.com", ResponseTimeWarning: 500, ResponseTimeCritical: 1000, ResponseTimeDuration: 5},
			},
			"2tZhm 1970-01-01 00:06:40 CRITICAL Example Domain https://example.com 2500.00 > 1000.00 msec, status:200",
		},
		{
			&alertSet{
				&mkr.Alert{ID: "2tZhm", Type: "expression", Status: "WARNING", MonitorID: "5rXR3", Value: 15.7, OpenedAt: 500},
				nil,
				&mkr.MonitorExpression{ID: "5rXR3", Type: "expression", Name: "Max loadavg5 monitor", Expression: "max(  \n  roleSlots(  \n    'service:role',\n    'loadavg5'\n  )\n)\n", Warning: 10.0, Critical: 20.0, Operator: ">"},
			},
			"2tZhm 1970-01-01 00:08:20 WARNING Max loadavg5 monitor max(roleSlots('service:role', 'loadavg5')) 15.70 > 10.00",
		},
	}

	for _, testCase := range testCases {
		str := formatJoinedAlert(testCase.alertSet, false)
		if str != testCase.want {
			t.Errorf("should be '%s' but got '%s'", testCase.want, str)
		}
	}
}
<|endoftext|>


<commit_before>package zkstore

import (
	"errors"
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/wandoulabs/codis/pkg/models"
)

var (
	ErrClosedZkStore = errors.New("use of closed zkstore")
	ErrAcquireAgain  = errors.New("acquire again")
	ErrReleaseAgain  = errors.New("release again")
	ErrNoProtection  = errors.New("operation without lock protection")
)

type ZkStore struct {
	sync.Mutex

	client *ZkClient
	prefix string

	locked bool
	closed bool
}

func NewStore(addr string, name string) (*ZkStore, error) {
	client, err := NewClient(addr, time.Minute)
	if err != nil {
		return nil, err
	}
	return &ZkStore{
		client: client,
		prefix: filepath.Join("/zk/codis2", name),
	}, nil
}

func (s *ZkStore) Close() error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil
	}
	s.closed = true

	s.client.Close()
	return nil
}

func (s *ZkStore) LockPath() string {
	return filepath.Join(s.prefix, "topom")
}

func (s *ZkStore) SlotPath(slotId int) string {
	return filepath.Join(s.prefix, "slots", fmt.Sprintf("slot-%04d", slotId))
}

func (s *ZkStore) ProxyBase() string {
	return filepath.Join(s.prefix, "proxy")
}

func (s *ZkStore) ProxyPath(proxyId int) string {
	return filepath.Join(s.prefix, "proxy", fmt.Sprintf("proxy-%4d", proxyId))
}

func (s *ZkStore) GroupBase() string {
	return filepath.Join(s.prefix, "group")
}

func (s *ZkStore) GroupPath(groupId int) string {
	return filepath.Join(s.prefix, "group", fmt.Sprintf("group-%04d", groupId))
}

func (s *ZkStore) Acquire(topom *models.Topom) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if s.locked {
		return ErrAcquireAgain
	}

	if err := s.client.Create(s.LockPath(), topom.Encode()); err != nil {
		return err
	}
	s.locked = true
	return nil
}

func (s *ZkStore) Release(force bool) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked && !force {
		return ErrReleaseAgain
	}

	if err := s.client.Delete(s.LockPath()); err != nil {
		return err
	}
	s.locked = false
	return nil
}

func (s *ZkStore) LoadSlotMapping(slotId int) (*models.SlotMapping, error) {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil, ErrClosedZkStore
	}
	if !s.locked {
		return nil, ErrNoProtection
	}

	b, err := s.client.LoadData(s.SlotPath(slotId))
	if err != nil {
		return nil, err
	}
	if b != nil {
		slot := &models.SlotMapping{}
		if err := slot.Decode(b); err != nil {
			return nil, err
		}
		return slot, nil
	}
	return nil, nil
}

func (s *ZkStore) SaveSlotMapping(slotId int, slot *models.SlotMapping) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Update(s.SlotPath(slotId), slot.Encode())
}

func (s *ZkStore) ListProxy() ([]*models.Proxy, error) {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil, ErrClosedZkStore
	}
	if !s.locked {
		return nil, ErrNoProtection
	}

	files, err := s.client.ListFile(s.ProxyBase())
	if err != nil {
		return nil, err
	}

	var plist []*models.Proxy
	for _, file := range files {
		b, err := s.client.LoadData(file)
		if err != nil {
			return nil, err
		}
		p := &models.Proxy{}
		if err := p.Decode(b); err != nil {
			return nil, err
		}
		plist = append(plist, p)
	}
	return plist, nil
}

func (s *ZkStore) CreateProxy(proxyId int, proxy *models.Proxy) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Create(s.ProxyPath(proxyId), proxy.Encode())
}

func (s *ZkStore) RemoveProxy(proxyId int) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Delete(s.ProxyPath(proxyId))
}

func (s *ZkStore) ListGroup() ([]*models.Group, error) {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil, ErrClosedZkStore
	}
	if !s.locked {
		return nil, ErrNoProtection
	}

	files, err := s.client.ListFile(s.GroupBase())
	if err != nil {
		return nil, err
	}

	var glist []*models.Group
	for _, file := range files {
		b, err := s.client.LoadData(file)
		if err != nil {
			return nil, err
		}
		g := &models.Group{}
		if err := g.Decode(b); err != nil {
			return nil, err
		}
		glist = append(glist, g)
	}
	return glist, nil
}

func (s *ZkStore) CreateGroup(groupId int, group *models.Group) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Create(s.GroupPath(groupId), group.Encode())
}

func (s *ZkStore) UpdateGroup(groupId int, group *models.Group) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Update(s.GroupPath(groupId), group.Encode())
}

func (s *ZkStore) RemoveGroup(groupId int) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Delete(s.GroupPath(groupId))
}
<commit_msg>Fix, zkstore will use proxy-%04d as proxy's node name<commit_after>package zkstore

import (
	"errors"
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/wandoulabs/codis/pkg/models"
)

var (
	ErrClosedZkStore = errors.New("use of closed zkstore")
	ErrAcquireAgain  = errors.New("acquire again")
	ErrReleaseAgain  = errors.New("release again")
	ErrNoProtection  = errors.New("operation without lock protection")
)

type ZkStore struct {
	sync.Mutex

	client *ZkClient
	prefix string

	locked bool
	closed bool
}

func NewStore(addr string, name string) (*ZkStore, error) {
	client, err := NewClient(addr, time.Minute)
	if err != nil {
		return nil, err
	}
	return &ZkStore{
		client: client,
		prefix: filepath.Join("/zk/codis2", name),
	}, nil
}

func (s *ZkStore) Close() error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil
	}
	s.closed = true

	s.client.Close()
	return nil
}

func (s *ZkStore) LockPath() string {
	return filepath.Join(s.prefix, "topom")
}

func (s *ZkStore) SlotPath(slotId int) string {
	return filepath.Join(s.prefix, "slots", fmt.Sprintf("slot-%04d", slotId))
}

func (s *ZkStore) ProxyBase() string {
	return filepath.Join(s.prefix, "proxy")
}

func (s *ZkStore) ProxyPath(proxyId int) string {
	return filepath.Join(s.prefix, "proxy", fmt.Sprintf("proxy-%04d", proxyId))
}

func (s *ZkStore) GroupBase() string {
	return filepath.Join(s.prefix, "group")
}

func (s *ZkStore) GroupPath(groupId int) string {
	return filepath.Join(s.prefix, "group", fmt.Sprintf("group-%04d", groupId))
}

func (s *ZkStore) Acquire(topom *models.Topom) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if s.locked {
		return ErrAcquireAgain
	}

	if err := s.client.Create(s.LockPath(), topom.Encode()); err != nil {
		return err
	}
	s.locked = true
	return nil
}

func (s *ZkStore) Release(force bool) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked && !force {
		return ErrReleaseAgain
	}

	if err := s.client.Delete(s.LockPath()); err != nil {
		return err
	}
	s.locked = false
	return nil
}

func (s *ZkStore) LoadSlotMapping(slotId int) (*models.SlotMapping, error) {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil, ErrClosedZkStore
	}
	if !s.locked {
		return nil, ErrNoProtection
	}

	b, err := s.client.LoadData(s.SlotPath(slotId))
	if err != nil {
		return nil, err
	}
	if b != nil {
		slot := &models.SlotMapping{}
		if err := slot.Decode(b); err != nil {
			return nil, err
		}
		return slot, nil
	}
	return nil, nil
}

func (s *ZkStore) SaveSlotMapping(slotId int, slot *models.SlotMapping) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Update(s.SlotPath(slotId), slot.Encode())
}

func (s *ZkStore) ListProxy() ([]*models.Proxy, error) {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil, ErrClosedZkStore
	}
	if !s.locked {
		return nil, ErrNoProtection
	}

	files, err := s.client.ListFile(s.ProxyBase())
	if err != nil {
		return nil, err
	}

	var plist []*models.Proxy
	for _, file := range files {
		b, err := s.client.LoadData(file)
		if err != nil {
			return nil, err
		}
		p := &models.Proxy{}
		if err := p.Decode(b); err != nil {
			return nil, err
		}
		plist = append(plist, p)
	}
	return plist, nil
}

func (s *ZkStore) CreateProxy(proxyId int, proxy *models.Proxy) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Create(s.ProxyPath(proxyId), proxy.Encode())
}

func (s *ZkStore) RemoveProxy(proxyId int) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Delete(s.ProxyPath(proxyId))
}

func (s *ZkStore) ListGroup() ([]*models.Group, error) {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return nil, ErrClosedZkStore
	}
	if !s.locked {
		return nil, ErrNoProtection
	}

	files, err := s.client.ListFile(s.GroupBase())
	if err != nil {
		return nil, err
	}

	var glist []*models.Group
	for _, file := range files {
		b, err := s.client.LoadData(file)
		if err != nil {
			return nil, err
		}
		g := &models.Group{}
		if err := g.Decode(b); err != nil {
			return nil, err
		}
		glist = append(glist, g)
	}
	return glist, nil
}

func (s *ZkStore) CreateGroup(groupId int, group *models.Group) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Create(s.GroupPath(groupId), group.Encode())
}

func (s *ZkStore) UpdateGroup(groupId int, group *models.Group) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Update(s.GroupPath(groupId), group.Encode())
}

func (s *ZkStore) RemoveGroup(groupId int) error {
	s.Lock()
	defer s.Unlock()
	if s.closed {
		return ErrClosedZkStore
	}
	if !s.locked {
		return ErrNoProtection
	}

	return s.client.Delete(s.GroupPath(groupId))
}
<|endoftext|>


<commit_before>// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.

// persist provides a 2-level GitHub cache: in-memory & persistent
package persist

import (
	"encoding/gob"
	"fmt"
	"os"
	"time"

	"github.com/google/go-github/v33/github"
	"github.com/google/triage-party/pkg/provider"
)

var inMemoryAge = 7 * 24 * time.Hour

// Config is cache configuration
type Config struct {
	Program string
	Type    string
	Path    string
}

type Blob struct {
	Created time.Time

	// Provider neutral fields, used by triage-party
	PullRequests        []*provider.PullRequest
	Issues              []*provider.Issue
	PullRequestComments []*provider.PullRequestComment
	IssueComments       []*provider.IssueComment
	Timeline            []*provider.Timeline
	Reviews             []*provider.PullRequestReview

	// Provider specific fields, used by other tramps
	GHPullRequest         *github.PullRequest
	GHCommitFiles         []*github.CommitFile
	GHPullRequestComments []*github.PullRequestComment
	GHIssueComments       []*github.IssueComment
	GHIssue               *github.Issue
}

// Cacher is the cache interface we support
type Cacher interface {
	String() string

	Set(string, *Blob) error
	Get(string, time.Time) *Blob

	Initialize() error
}

func New(cfg Config) (Cacher, error) {
	gob.Register(&Blob{})
	switch cfg.Type {
	case "mysql":
		return NewMySQL(cfg)
	case "cloudsql":
		return NewCloudSQL(cfg)
	case "postgres":
		return NewPostgres(cfg)
	case "disk", "":
		return NewDisk(cfg)
	case "memory":
		return NewMemory(cfg)
	default:
		return nil, fmt.Errorf("unknown backend: %q", cfg.Type)
	}
}

// FromEnv is shared magic between binaries
func FromEnv(program string, backend string, path string) (Cacher, error) {
	if backend == "" {
		backend = os.Getenv("PERSIST_BACKEND")
	}
	if backend == "" {
		backend = "disk"
	}

	if path == "" {
		path = os.Getenv("PERSIST_PATH")
	}

	if program == "" {
		program = "triage-party"
	}

	c, err := New(Config{
		Program: program,
		Type:    backend,
		Path:    path,
	})

	if err != nil {
		return nil, fmt.Errorf("new from %s: %s: %w", backend, path, err)
	}
	return c, nil
}
<commit_msg>Add ghtimeline struct member<commit_after>// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.

// persist provides a 2-level GitHub cache: in-memory & persistent
package persist

import (
	"encoding/gob"
	"fmt"
	"os"
	"time"

	"github.com/google/go-github/v33/github"
	"github.com/google/triage-party/pkg/provider"
)

var inMemoryAge = 7 * 24 * time.Hour

// Config is cache configuration
type Config struct {
	Program string
	Type    string
	Path    string
}

type Blob struct {
	Created time.Time

	// Provider neutral fields, used by triage-party
	PullRequests        []*provider.PullRequest
	Issues              []*provider.Issue
	PullRequestComments []*provider.PullRequestComment
	IssueComments       []*provider.IssueComment
	Timeline            []*provider.Timeline
	Reviews             []*provider.PullRequestReview

	// Provider specific fields, used by other tramps
	GHPullRequest         *github.PullRequest
	GHCommitFiles         []*github.CommitFile
	GHPullRequestComments []*github.PullRequestComment
	GHIssueComments       []*github.IssueComment
	GHIssue               *github.Issue
	GHTimeline            []*github.Timeline
}

// Cacher is the cache interface we support
type Cacher interface {
	String() string

	Set(string, *Blob) error
	Get(string, time.Time) *Blob

	Initialize() error
}

func New(cfg Config) (Cacher, error) {
	gob.Register(&Blob{})
	switch cfg.Type {
	case "mysql":
		return NewMySQL(cfg)
	case "cloudsql":
		return NewCloudSQL(cfg)
	case "postgres":
		return NewPostgres(cfg)
	case "disk", "":
		return NewDisk(cfg)
	case "memory":
		return NewMemory(cfg)
	default:
		return nil, fmt.Errorf("unknown backend: %q", cfg.Type)
	}
}

// FromEnv is shared magic between binaries
func FromEnv(program string, backend string, path string) (Cacher, error) {
	if backend == "" {
		backend = os.Getenv("PERSIST_BACKEND")
	}
	if backend == "" {
		backend = "disk"
	}

	if path == "" {
		path = os.Getenv("PERSIST_PATH")
	}

	if program == "" {
		program = "triage-party"
	}

	c, err := New(Config{
		Program: program,
		Type:    backend,
		Path:    path,
	})

	if err != nil {
		return nil, fmt.Errorf("new from %s: %s: %w", backend, path, err)
	}
	return c, nil
}
<|endoftext|>


<commit_before>package opentracing

import (
	"context"
	"io"
	"net/http"
	"time"

	"github.com/hellofresh/gcloud-opentracing"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/errors"
	"github.com/opentracing/opentracing-go"
	log "github.com/sirupsen/logrus"
	jaeger "github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-lib/metrics"
)

type noopCloser struct{}

func (n noopCloser) Close() error { return nil }

// Build a tracer based on the configuration provided
func Build(config config.Tracing) (opentracing.Tracer, io.Closer, error) {
	switch config.Tracer {
	case "gcloud":
		log.Debug("Using google cloud platform (stackdriver trace) as tracing system")
		tracer, err := buildGCloud(config.GoogleCloudTracing)
		return tracer, noopCloser{}, err
	case "jaeger":
		return buildJaeger(config.JaegerTracing)
	default:
		log.Debug("No tracer selected")
		return &opentracing.NoopTracer{}, noopCloser{}, nil
	}
}

// FromContext creates a span from a context that contains a parent span
func FromContext(ctx context.Context, name string) opentracing.Span {
	span, _ := opentracing.StartSpanFromContext(ctx, name)
	return span
}

// ToContext sets a span to a context
func ToContext(r *http.Request, span opentracing.Span) *http.Request {
	return r.WithContext(opentracing.ContextWithSpan(r.Context(), span))
}

func buildGCloud(config config.GoogleCloudTracing) (opentracing.Tracer, error) {
	return gcloudtracer.NewTracer(
		context.Background(),
		gcloudtracer.WithLogger(log.StandardLogger()),
		gcloudtracer.WithProject(config.ProjectID),
		gcloudtracer.WithJWTCredentials(gcloudtracer.JWTCredentials{
			Email:        config.Email,
			PrivateKey:   []byte(config.PrivateKey),
			PrivateKeyID: config.PrivateKeyID,
		}),
	)
}

func buildJaeger(c config.JaegerTracing) (opentracing.Tracer, io.Closer, error) {
	bufferFLushInterval, err := time.ParseDuration(c.BufferFlushInterval)
	if err != nil {
		return nil, noopCloser{}, errors.Wrap(err, "could not parse buffer flush interval for jaeger")
	}

	cfg := jaegercfg.Configuration{
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:            c.LogSpans,
			BufferFlushInterval: bufferFLushInterval,
			LocalAgentHostPort:  c.DSN,
			QueueSize:           c.QueueSize,
		},
	}

	return cfg.New(
		c.ServiceName,
		jaegercfg.Logger(jaegerLoggerAdapter{log.StandardLogger()}),
		jaegercfg.Metrics(metrics.NullFactory),
	)
}
<commit_msg>Making const out of the strings<commit_after>package opentracing

import (
	"context"
	"io"
	"net/http"
	"time"

	"github.com/hellofresh/gcloud-opentracing"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/errors"
	"github.com/opentracing/opentracing-go"
	log "github.com/sirupsen/logrus"
	jaeger "github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-lib/metrics"
)

const (
	gcloudTracing = "gcloud"
	jaegerTracing = "jaeger"
)

type noopCloser struct{}

func (n noopCloser) Close() error { return nil }

// Build a tracer based on the configuration provided
func Build(config config.Tracing) (opentracing.Tracer, io.Closer, error) {
	switch config.Tracer {
	case gcloudTracing:
		log.Debug("Using google cloud platform (stackdriver trace) as tracing system")
		tracer, err := buildGCloud(config.GoogleCloudTracing)
		return tracer, noopCloser{}, err
	case jaegerTracing:
		return buildJaeger(config.JaegerTracing)
	default:
		log.Debug("No tracer selected")
		return &opentracing.NoopTracer{}, noopCloser{}, nil
	}
}

// FromContext creates a span from a context that contains a parent span
func FromContext(ctx context.Context, name string) opentracing.Span {
	span, _ := opentracing.StartSpanFromContext(ctx, name)
	return span
}

// ToContext sets a span to a context
func ToContext(r *http.Request, span opentracing.Span) *http.Request {
	return r.WithContext(opentracing.ContextWithSpan(r.Context(), span))
}

func buildGCloud(config config.GoogleCloudTracing) (opentracing.Tracer, error) {
	return gcloudtracer.NewTracer(
		context.Background(),
		gcloudtracer.WithLogger(log.StandardLogger()),
		gcloudtracer.WithProject(config.ProjectID),
		gcloudtracer.WithJWTCredentials(gcloudtracer.JWTCredentials{
			Email:        config.Email,
			PrivateKey:   []byte(config.PrivateKey),
			PrivateKeyID: config.PrivateKeyID,
		}),
	)
}

func buildJaeger(c config.JaegerTracing) (opentracing.Tracer, io.Closer, error) {
	bufferFLushInterval, err := time.ParseDuration(c.BufferFlushInterval)
	if err != nil {
		return nil, noopCloser{}, errors.Wrap(err, "could not parse buffer flush interval for jaeger")
	}

	cfg := jaegercfg.Configuration{
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:            c.LogSpans,
			BufferFlushInterval: bufferFLushInterval,
			LocalAgentHostPort:  c.DSN,
			QueueSize:           c.QueueSize,
		},
	}

	return cfg.New(
		c.ServiceName,
		jaegercfg.Logger(jaegerLoggerAdapter{log.StandardLogger()}),
		jaegercfg.Metrics(metrics.NullFactory),
	)
}
<|endoftext|>


<commit_before>// Copyright 2016-2018 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 policy

import (
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/maps/policymap"

	"github.com/sirupsen/logrus"
)

// Consumable holds all of the policies relevant to this security identity,
// including label-based policies, L4Policy, and L7 policy. A Consumable is
// shared amongst all endpoints on the same node which possess the same security
// identity.
type Consumable struct {
	// ID of the consumable (same as security ID)
	ID identity.NumericIdentity `json:"id"`
	// Mutex protects all variables from this structure below this line
	Mutex lock.RWMutex
	// Labels are the SecurityIdentity of this consumable
	Labels *identity.Identity `json:"labels"`
	// LabelArray contains the same labels from identity in a form of a list, used for faster lookup
	LabelArray labels.LabelArray `json:"-"`
	// Iteration policy of the Consumable
	Iteration uint64 `json:"-"`

	// PolicyMaps maps the file descriptor of the BPF PolicyMap in the BPF filesystem
	// to the golang representation of the same BPF PolicyPap. Each key-value
	// pair corresponds to the BPF PolicyMap for a given endpoint.
	PolicyMaps map[int]*policymap.PolicyMap `json:"-"`

	// IngressIdentities is the set of security identities from which ingress
	// traffic is allowed. The value corresponds to whether the corresponding
	// key (security identity) should be garbage collected upon policy calculation.
	IngressIdentities map[identity.NumericIdentity]bool `json:"ingress-identities"`

	// EgressMaps maps the file descriptor of the BPF PolicyMap in the BPF filesystem
	// to the golang representation of the same BPF PolicyPap. Each key-value
	// pair corresponds to the BPF PolicyMap for a given endpoint.
	EgressMaps map[int]*policymap.PolicyMap `json:"-"`

	// EgressIdentities is the set of security identities from which egress
	// traffic is allowed. The value corresponds to whether the corresponding
	// key (security identity) should be garbage collected upon policy calculation.
	EgressIdentities map[identity.NumericIdentity]bool `json:"egress-identities"`

	// L4Policy contains the policy of this consumable
	L4Policy *L4Policy `json:"l4-policy"`
	// L3L4Policy contains the L3, L4 and L7 ingress policy of this consumable
	L3L4Policy *SecurityIDContexts `json:"l3-l4-policy"`
	cache      *ConsumableCache
}

// NewConsumable creates a new consumable
func NewConsumable(id identity.NumericIdentity, lbls *identity.Identity, cache *ConsumableCache) *Consumable {
	consumable := &Consumable{
		ID:                id,
		Iteration:         0,
		Labels:            lbls,
		PolicyMaps:        map[int]*policymap.PolicyMap{},
		IngressIdentities: map[identity.NumericIdentity]bool{},
		EgressIdentities:  map[identity.NumericIdentity]bool{},
		cache:             cache,
	}
	if lbls != nil {
		consumable.LabelArray = lbls.Labels.ToSlice()
	}

	return consumable
}

// AddMap adds m to the Consumable's PolicyMaps. This represents
// the PolicyMap being added for a specific endpoint.
func (c *Consumable) AddMap(m *policymap.PolicyMap) {
	c.Mutex.Lock()
	defer c.Mutex.Unlock()
	if c.PolicyMaps == nil {
		c.PolicyMaps = make(map[int]*policymap.PolicyMap)
	}

	// Check if map is already associated with this consumable
	if _, ok := c.PolicyMaps[m.Fd]; ok {
		return
	}

	log.WithFields(logrus.Fields{
		"policymap":  m,
		"consumable": c,
	}).Debug("Adding policy map to consumable")
	c.PolicyMaps[m.Fd] = m

	// Populate the new map with the already established allowed identities from
	// which ingress traffic is allowed.
	for ingressIdentity := range c.IngressIdentities {
		if err := m.AllowIdentity(ingressIdentity.Uint32(), policymap.Ingress); err != nil {
			log.WithError(err).Warn("Update of policy map failed")
		}
	}

	for egressIdentity := range c.EgressIdentities {
		if err := m.AllowIdentity(egressIdentity.Uint32(), policymap.Egress); err != nil {
			log.WithError(err).Warn("Update of policy map failed")
		}
	}
}

func (c *Consumable) delete() {

	for ingressIdentity := range c.IngressIdentities {
		// FIXME: This explicit removal could be removed eventually to
		// speed things up as the policy map should get deleted anyway.
		c.removeFromMaps(ingressIdentity, policymap.Ingress)
	}

	for egressIdentity := range c.EgressIdentities {
		// FIXME: This explicit removal could be removed eventually to
		// speed things up as the policy map should get deleted anyway.
		c.removeFromMaps(egressIdentity, policymap.Egress)
	}

	if c.cache != nil {
		c.cache.Remove(c)
	}
}

// RemovePolicyMap removes m from the Consumable's PolicyMaps. This represents
// the PolicyMap being deleted for a specific endpoint.
func (c *Consumable) RemovePolicyMap(m *policymap.PolicyMap) {
	if m != nil {
		c.Mutex.Lock()
		delete(c.PolicyMaps, m.Fd)
		log.WithFields(logrus.Fields{
			"policymap":  m,
			"consumable": c,
			"count":      len(c.PolicyMaps),
		}).Debug("Removing policy map from consumable")

		// If there are no more PolicyMaps for this Consumable, then the
		// Consumable is no longer needed and should be removed from the cache,
		// and all cross references must be undone.
		if len(c.PolicyMaps) == 0 {
			c.delete()
		}
		c.Mutex.Unlock()
	}

}

func (c *Consumable) addToPolicyMaps(id identity.NumericIdentity, trafficDirection policymap.TrafficDirection) {
	for _, m := range c.PolicyMaps {
		if m.IdentityExists(id.Uint32(), trafficDirection) {
			continue
		}

		scopedLog := log.WithFields(logrus.Fields{
			"policymap":                m,
			logfields.Identity:         id,
			logfields.TrafficDirection: trafficDirection.String(),
		})

		scopedLog.Debug("Updating policy BPF map: allowing Identity")
		if err := m.AllowIdentity(id.Uint32(), trafficDirection); err != nil {
			scopedLog.WithError(err).Warn("Update of policy map failed")
		}
	}
}

// A rule is the 'last rule' for an identity if it does not exist as a key
// in any of the maps for this Consumable.
func (c *Consumable) wasLastRule(id identity.NumericIdentity) bool {
	_, existsIngressIdentity := c.IngressIdentities[id]
	_, existsEgressIdentity := c.EgressIdentities[id]
	return !existsIngressIdentity && !existsEgressIdentity
}

func (c *Consumable) removeFromMaps(id identity.NumericIdentity, trafficDirection policymap.TrafficDirection) {
	for _, m := range c.PolicyMaps {
		scopedLog := log.WithFields(logrus.Fields{
			"policymap":                m,
			logfields.Identity:         id,
			logfields.TrafficDirection: trafficDirection.String(),
		})

		scopedLog.Debug("Updating policy BPF map: denying Identity")
		if err := m.DeleteIdentity(id.Uint32(), trafficDirection); err != nil {
			scopedLog.WithError(err).Warn("Update of policy map failed")
		}
	}
}

// AllowIngressIdentityLocked adds the given security identity to the Consumable's
// IngressIdentities map. Must be called with Consumable mutex Locked.
// Returns true if the identity was not present in this Consumable's
// IngressIdentities map, and thus had to be added, false if it is already added.
func (c *Consumable) AllowIngressIdentityLocked(cache *ConsumableCache, id identity.NumericIdentity) bool {
	_, exists := c.IngressIdentities[id]
	if !exists {
		log.WithFields(logrus.Fields{
			logfields.Identity: id,
			"consumable":       logfields.Repr(c),
		}).Debug("Allowing security identity on ingress for consumable")
		c.addToPolicyMaps(id, policymap.Ingress)

		// If id corresponds to a reserved identity, Consumable corresponding to
		// that security identity needs to be updated explicitly, as reserved
		// identities do not have a corresponding endpoint for which policy
		// recalculation (when Consumables are updated) is done.
		if id.IsReservedIdentity() {
			reservedConsumable := cache.Lookup(id)
			if reservedConsumable != nil {
				// If we are accessing the same Consumable (allowing traffic
				// to itself), we don't need to take its mutex because it was
				// already taken before calling this function.
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.AllowIngressIdentityLocked(cache, c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.AllowIngressIdentityLocked(cache, c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to allow ingress from identity %d", c.ID)
			}
		}
	}

	c.IngressIdentities[id] = true

	return !exists // not changed, was already in map.
}

// AllowEgressIdentityLocked adds the given security identity to the Consumable's
// EgressIdentities map. Must be called with Consumable mutex Locked.
// Returns true if the identity was not present in this Consumable's
// EgressIdentities map, and thus had to be added, false if it is already added.
func (c *Consumable) AllowEgressIdentityLocked(cache *ConsumableCache, id identity.NumericIdentity) bool {
	_, exists := c.EgressIdentities[id]
	if !exists {
		log.WithFields(logrus.Fields{
			logfields.Identity: id,
			"consumable":       logfields.Repr(c),
		}).Debug("New egress security identity for consumable")

		c.addToPolicyMaps(id, policymap.Egress)

		// If id corresponds to a reserved identity, Consumable corresponding to
		// that security identity needs to be updated explicitly, as reserved
		// identities do not have a corresponding endpoint for which policy
		// recalculation (when Consumables are updated) is done.
		if id.IsReservedIdentity() {
			reservedConsumable := cache.Lookup(id)
			if reservedConsumable != nil {
				// Avoid deadlock ; is this necessary? Being cautious.
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.AllowEgressIdentityLocked(cache, c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.AllowEgressIdentityLocked(cache, c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to allow egress to identity %d", c.ID)
			}
		}
	}
	c.EgressIdentities[id] = true
	return !exists // not changed.
}

// RemoveIngressIdentityLocked removes the given security identity from Consumable's
// IngressIdentities map.
// Must be called with the Consumable mutex locked.
func (c *Consumable) RemoveIngressIdentityLocked(id identity.NumericIdentity) {
	if _, ok := c.IngressIdentities[id]; ok {
		log.WithField(logfields.Identity, id).Debug("Removing identity from ingress map")
		delete(c.IngressIdentities, id)

		// Consumables corresponding to reserved identities need to be updated
		// explicitly because they are not updated or regenerated.
		if id.IsReservedIdentity() {
			reservedConsumable := c.cache.Lookup(id)
			if reservedConsumable != nil {
				// If we are accessing the same Consumable (allowing traffic
				// to itself), we don't need to take its mutex because it was
				// already taken before calling this function.
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.RemoveIngressIdentityLocked(c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.RemoveIngressIdentityLocked(c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to disallow ingress from identity %d", c.ID)
			}

		}
		c.removeFromMaps(id, policymap.Ingress)
	}
}

// RemoveEgressIdentityLocked removes the given security identity from Consumable's
// EgressIdentities map.
// Must be called with the Consumable mutex locked.
func (c *Consumable) RemoveEgressIdentityLocked(id identity.NumericIdentity) {
	if _, ok := c.EgressIdentities[id]; ok {
		log.WithField(logfields.Identity, id).Debug("Removing egress identity")
		delete(c.EgressIdentities, id)

		// Consumables corresponding to reserved identities need to be updated
		// explicitly because they are not updated or regenerated.
		if id.IsReservedIdentity() {
			reservedConsumable := c.cache.Lookup(id)
			if reservedConsumable != nil {
				// Avoid deadlock!
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.RemoveEgressIdentityLocked(c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.RemoveEgressIdentityLocked(c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to disallow egress to identity %d", c.ID)
			}
		}

		c.removeFromMaps(id, policymap.Egress)
	}
}

// AllowsIngress returns whether id is is in the list of allowed identities
// from which ingress traffic is allowed for this Consumable.
func (c *Consumable) AllowsIngress(id identity.NumericIdentity) bool {
	c.Mutex.RLock()
	isIdentityAllowed, _ := c.IngressIdentities[id]
	c.Mutex.RUnlock()
	return isIdentityAllowed
}
<commit_msg>pkg/policy/consumer.go: misc. comment updates for Consumable<commit_after>// Copyright 2016-2018 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 policy

import (
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/maps/policymap"

	"github.com/sirupsen/logrus"
)

// Consumable holds all of the policies relevant to this security identity,
// including label-based policies, L4Policy, and L7 policy. A Consumable is
// shared amongst all endpoints on the same node which possess the same security
// identity.
type Consumable struct {
	// ID of the consumable (same as security ID)
	ID identity.NumericIdentity `json:"id"`
	// Mutex protects all variables from this structure below this line
	Mutex lock.RWMutex

	// Labels are the SecurityIdentity of this consumable
	Labels *identity.Identity `json:"labels"`

	// LabelArray contains the same labels from identity in a form of a list, used for faster lookup
	LabelArray labels.LabelArray `json:"-"`

	// Iteration policy of the Consumable
	Iteration uint64 `json:"-"`

	// PolicyMaps maps the file descriptor of the BPF PolicyMap in the BPF filesystem
	// to the golang representation of the same BPF PolicyPap. Each key-value
	// pair corresponds to the BPF PolicyMap for a given endpoint.
	PolicyMaps map[int]*policymap.PolicyMap `json:"-"`

	// IngressIdentities is the set of security identities from which ingress
	// traffic is allowed. The value corresponds to whether the corresponding
	// key (security identity) should be garbage collected upon policy calculation.
	IngressIdentities map[identity.NumericIdentity]bool `json:"ingress-identities"`

	// EgressMaps maps the file descriptor of the BPF PolicyMap in the BPF filesystem
	// to the golang representation of the same BPF PolicyPap. Each key-value
	// pair corresponds to the BPF PolicyMap for a given endpoint.
	EgressMaps map[int]*policymap.PolicyMap `json:"-"`

	// EgressIdentities is the set of security identities from which egress
	// traffic is allowed. The value corresponds to whether the corresponding
	// key (security identity) should be garbage collected upon policy calculation.
	EgressIdentities map[identity.NumericIdentity]bool `json:"egress-identities"`

	// L4Policy contains the L4-related policy of this consumable
	// (label-dependent L4, and L4-dependent L7 policy).
	L4Policy *L4Policy `json:"l4-policy"`

	// L3L4Policy contains the L3, L4 and L7 ingress policy of this consumable
	L3L4Policy *SecurityIDContexts `json:"l3-l4-policy"`

	cache *ConsumableCache
}

// NewConsumable creates a new consumable
func NewConsumable(id identity.NumericIdentity, lbls *identity.Identity, cache *ConsumableCache) *Consumable {
	consumable := &Consumable{
		ID:                id,
		Iteration:         0,
		Labels:            lbls,
		PolicyMaps:        map[int]*policymap.PolicyMap{},
		IngressIdentities: map[identity.NumericIdentity]bool{},
		EgressIdentities:  map[identity.NumericIdentity]bool{},
		cache:             cache,
	}
	if lbls != nil {
		consumable.LabelArray = lbls.Labels.ToSlice()
	}

	return consumable
}

// AddMap adds m to the Consumable's PolicyMaps. This represents
// the PolicyMap being added for a specific endpoint.
func (c *Consumable) AddMap(m *policymap.PolicyMap) {
	c.Mutex.Lock()
	defer c.Mutex.Unlock()
	if c.PolicyMaps == nil {
		c.PolicyMaps = make(map[int]*policymap.PolicyMap)
	}

	// Check if map is already associated with this consumable
	if _, ok := c.PolicyMaps[m.Fd]; ok {
		return
	}

	log.WithFields(logrus.Fields{
		"policymap":  m,
		"consumable": c,
	}).Debug("Adding policy map to consumable")
	c.PolicyMaps[m.Fd] = m

	// Populate the new map with the already established allowed identities from
	// which ingress traffic is allowed.
	for ingressIdentity := range c.IngressIdentities {
		if err := m.AllowIdentity(ingressIdentity.Uint32(), policymap.Ingress); err != nil {
			log.WithError(err).Warn("Update of policy map failed")
		}
	}

	for egressIdentity := range c.EgressIdentities {
		if err := m.AllowIdentity(egressIdentity.Uint32(), policymap.Egress); err != nil {
			log.WithError(err).Warn("Update of policy map failed")
		}
	}
}

func (c *Consumable) delete() {

	for ingressIdentity := range c.IngressIdentities {
		// FIXME: This explicit removal could be removed eventually to
		// speed things up as the policy map should get deleted anyway.
		c.removeFromMaps(ingressIdentity, policymap.Ingress)
	}

	for egressIdentity := range c.EgressIdentities {
		// FIXME: This explicit removal could be removed eventually to
		// speed things up as the policy map should get deleted anyway.
		c.removeFromMaps(egressIdentity, policymap.Egress)
	}

	if c.cache != nil {
		c.cache.Remove(c)
	}
}

// RemovePolicyMap removes m from the Consumable's PolicyMaps. This represents
// the PolicyMap being deleted for a specific endpoint.
func (c *Consumable) RemovePolicyMap(m *policymap.PolicyMap) {
	if m != nil {
		c.Mutex.Lock()
		delete(c.PolicyMaps, m.Fd)
		log.WithFields(logrus.Fields{
			"policymap":  m,
			"consumable": c,
			"count":      len(c.PolicyMaps),
		}).Debug("Removing policy map from consumable")

		// If there are no more PolicyMaps for this Consumable, then the
		// Consumable is no longer needed and should be removed from the cache,
		// and all cross references must be undone.
		if len(c.PolicyMaps) == 0 {
			c.delete()
		}
		c.Mutex.Unlock()
	}

}

func (c *Consumable) addToPolicyMaps(id identity.NumericIdentity, trafficDirection policymap.TrafficDirection) {
	for _, m := range c.PolicyMaps {
		if m.IdentityExists(id.Uint32(), trafficDirection) {
			continue
		}

		scopedLog := log.WithFields(logrus.Fields{
			"policymap":                m,
			logfields.Identity:         id,
			logfields.TrafficDirection: trafficDirection.String(),
		})

		scopedLog.Debug("Updating policy BPF map: allowing Identity")
		if err := m.AllowIdentity(id.Uint32(), trafficDirection); err != nil {
			scopedLog.WithError(err).Warn("Update of policy map failed")
		}
	}
}

// A rule is the 'last rule' for an identity if it does not exist as a key
// in any of the maps for this Consumable.
func (c *Consumable) wasLastRule(id identity.NumericIdentity) bool {
	_, existsIngressIdentity := c.IngressIdentities[id]
	_, existsEgressIdentity := c.EgressIdentities[id]
	return !existsIngressIdentity && !existsEgressIdentity
}

func (c *Consumable) removeFromMaps(id identity.NumericIdentity, trafficDirection policymap.TrafficDirection) {
	for _, m := range c.PolicyMaps {
		scopedLog := log.WithFields(logrus.Fields{
			"policymap":                m,
			logfields.Identity:         id,
			logfields.TrafficDirection: trafficDirection.String(),
		})

		scopedLog.Debug("Updating policy BPF map: denying Identity")
		if err := m.DeleteIdentity(id.Uint32(), trafficDirection); err != nil {
			scopedLog.WithError(err).Warn("Update of policy map failed")
		}
	}
}

// AllowIngressIdentityLocked adds the given security identity to the Consumable's
// IngressIdentities map. Must be called with Consumable mutex Locked.
// Returns true if the identity was not present in this Consumable's
// IngressIdentities map, and thus had to be added, false if it is already added.
func (c *Consumable) AllowIngressIdentityLocked(cache *ConsumableCache, id identity.NumericIdentity) bool {
	_, exists := c.IngressIdentities[id]
	if !exists {
		log.WithFields(logrus.Fields{
			logfields.Identity: id,
			"consumable":       logfields.Repr(c),
		}).Debug("Allowing security identity on ingress for consumable")
		c.addToPolicyMaps(id, policymap.Ingress)

		// If id corresponds to a reserved identity, Consumable corresponding to
		// that security identity needs to be updated explicitly, as reserved
		// identities do not have a corresponding endpoint for which policy
		// recalculation (when Consumables are updated) is done.
		if id.IsReservedIdentity() {
			reservedConsumable := cache.Lookup(id)
			if reservedConsumable != nil {
				// If we are accessing the same Consumable (allowing traffic
				// to itself), we don't need to take its mutex because it was
				// already taken before calling this function.
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.AllowIngressIdentityLocked(cache, c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.AllowIngressIdentityLocked(cache, c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to allow ingress from identity %d", c.ID)
			}
		}
	}

	c.IngressIdentities[id] = true

	return !exists // not changed, was already in map.
}

// AllowEgressIdentityLocked adds the given security identity to the Consumable's
// EgressIdentities map. Must be called with Consumable mutex Locked.
// Returns true if the identity was not present in this Consumable's
// EgressIdentities map, and thus had to be added, false if it is already added.
func (c *Consumable) AllowEgressIdentityLocked(cache *ConsumableCache, id identity.NumericIdentity) bool {
	_, exists := c.EgressIdentities[id]
	if !exists {
		log.WithFields(logrus.Fields{
			logfields.Identity: id,
			"consumable":       logfields.Repr(c),
		}).Debug("New egress security identity for consumable")

		c.addToPolicyMaps(id, policymap.Egress)

		// If id corresponds to a reserved identity, Consumable corresponding to
		// that security identity needs to be updated explicitly, as reserved
		// identities do not have a corresponding endpoint for which policy
		// recalculation (when Consumables are updated) is done.
		if id.IsReservedIdentity() {
			reservedConsumable := cache.Lookup(id)
			if reservedConsumable != nil {
				// Avoid deadlock ; is this necessary? Being cautious.
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.AllowEgressIdentityLocked(cache, c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.AllowEgressIdentityLocked(cache, c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to allow egress to identity %d", c.ID)
			}
		}
	}
	c.EgressIdentities[id] = true
	return !exists // not changed.
}

// RemoveIngressIdentityLocked removes the given security identity from Consumable's
// IngressIdentities map.
// Must be called with the Consumable mutex locked.
func (c *Consumable) RemoveIngressIdentityLocked(id identity.NumericIdentity) {
	if _, ok := c.IngressIdentities[id]; ok {
		log.WithField(logfields.Identity, id).Debug("Removing identity from ingress map")
		delete(c.IngressIdentities, id)

		// Consumables corresponding to reserved identities need to be updated
		// explicitly because they are not updated or regenerated.
		if id.IsReservedIdentity() {
			reservedConsumable := c.cache.Lookup(id)
			if reservedConsumable != nil {
				// If we are accessing the same Consumable (allowing traffic
				// to itself), we don't need to take its mutex because it was
				// already taken before calling this function.
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.RemoveIngressIdentityLocked(c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.RemoveIngressIdentityLocked(c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to disallow ingress from identity %d", c.ID)
			}

		}
		c.removeFromMaps(id, policymap.Ingress)
	}
}

// RemoveEgressIdentityLocked removes the given security identity from Consumable's
// EgressIdentities map.
// Must be called with the Consumable mutex locked.
func (c *Consumable) RemoveEgressIdentityLocked(id identity.NumericIdentity) {
	if _, ok := c.EgressIdentities[id]; ok {
		log.WithField(logfields.Identity, id).Debug("Removing egress identity")
		delete(c.EgressIdentities, id)

		// Consumables corresponding to reserved identities need to be updated
		// explicitly because they are not updated or regenerated.
		if id.IsReservedIdentity() {
			reservedConsumable := c.cache.Lookup(id)
			if reservedConsumable != nil {
				// Avoid deadlock!
				if id != c.ID {
					reservedConsumable.Mutex.Lock()
					reservedConsumable.RemoveEgressIdentityLocked(c.ID)
					reservedConsumable.Mutex.Unlock()
				} else {
					reservedConsumable.RemoveEgressIdentityLocked(c.ID)
				}
			} else {
				log.WithField(logfields.Identity, id).Warningf("unable to disallow egress to identity %d", c.ID)
			}
		}

		c.removeFromMaps(id, policymap.Egress)
	}
}

// AllowsIngress returns whether id is is in the list of allowed identities
// from which ingress traffic is allowed for this Consumable.
func (c *Consumable) AllowsIngress(id identity.NumericIdentity) bool {
	c.Mutex.RLock()
	isIdentityAllowed, _ := c.IngressIdentities[id]
	c.Mutex.RUnlock()
	return isIdentityAllowed
}
<|endoftext|>