<commit_before>package system // import "github.com/docker/docker/pkg/system"

import (
	"syscall"
	"unsafe"

	"github.com/Microsoft/hcsshim/osversion"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/windows"
)

const (
	OWNER_SECURITY_INFORMATION               = windows.OWNER_SECURITY_INFORMATION     // Deprecated: use golang.org/x/sys/windows.OWNER_SECURITY_INFORMATION
	GROUP_SECURITY_INFORMATION               = windows.GROUP_SECURITY_INFORMATION     // Deprecated: use golang.org/x/sys/windows.GROUP_SECURITY_INFORMATION
	DACL_SECURITY_INFORMATION                = windows.DACL_SECURITY_INFORMATION      // Deprecated: use golang.org/x/sys/windows.DACL_SECURITY_INFORMATION
	SACL_SECURITY_INFORMATION                = windows.SACL_SECURITY_INFORMATION      // Deprecated: use golang.org/x/sys/windows.SACL_SECURITY_INFORMATION
	LABEL_SECURITY_INFORMATION               = windows.LABEL_SECURITY_INFORMATION     // Deprecated: use golang.org/x/sys/windows.LABEL_SECURITY_INFORMATION
	ATTRIBUTE_SECURITY_INFORMATION           = windows.ATTRIBUTE_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.ATTRIBUTE_SECURITY_INFORMATION
	SCOPE_SECURITY_INFORMATION               = windows.SCOPE_SECURITY_INFORMATION     // Deprecated: use golang.org/x/sys/windows.SCOPE_SECURITY_INFORMATION
	PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
	ACCESS_FILTER_SECURITY_INFORMATION       = 0x00000100
	BACKUP_SECURITY_INFORMATION              = windows.BACKUP_SECURITY_INFORMATION           // Deprecated: use golang.org/x/sys/windows.BACKUP_SECURITY_INFORMATION
	PROTECTED_DACL_SECURITY_INFORMATION      = windows.PROTECTED_DACL_SECURITY_INFORMATION   // Deprecated: use golang.org/x/sys/windows.PROTECTED_DACL_SECURITY_INFORMATION
	PROTECTED_SACL_SECURITY_INFORMATION      = windows.PROTECTED_SACL_SECURITY_INFORMATION   // Deprecated: use golang.org/x/sys/windows.PROTECTED_SACL_SECURITY_INFORMATION
	UNPROTECTED_DACL_SECURITY_INFORMATION    = windows.UNPROTECTED_DACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.UNPROTECTED_DACL_SECURITY_INFORMATION
	UNPROTECTED_SACL_SECURITY_INFORMATION    = windows.UNPROTECTED_SACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.UNPROTECTED_SACL_SECURITY_INFORMATION
)

const (
	SE_UNKNOWN_OBJECT_TYPE     = windows.SE_UNKNOWN_OBJECT_TYPE     // Deprecated: use golang.org/x/sys/windows.SE_UNKNOWN_OBJECT_TYPE
	SE_FILE_OBJECT             = windows.SE_FILE_OBJECT             // Deprecated: use golang.org/x/sys/windows.SE_FILE_OBJECT
	SE_SERVICE                 = windows.SE_SERVICE                 // Deprecated: use golang.org/x/sys/windows.SE_SERVICE
	SE_PRINTER                 = windows.SE_PRINTER                 // Deprecated: use golang.org/x/sys/windows.SE_PRINTER
	SE_REGISTRY_KEY            = windows.SE_REGISTRY_KEY            // Deprecated: use golang.org/x/sys/windows.SE_REGISTRY_KEY
	SE_LMSHARE                 = windows.SE_LMSHARE                 // Deprecated: use golang.org/x/sys/windows.SE_LMSHARE
	SE_KERNEL_OBJECT           = windows.SE_KERNEL_OBJECT           // Deprecated: use golang.org/x/sys/windows.SE_KERNEL_OBJECT
	SE_WINDOW_OBJECT           = windows.SE_WINDOW_OBJECT           // Deprecated: use golang.org/x/sys/windows.SE_WINDOW_OBJECT
	SE_DS_OBJECT               = windows.SE_DS_OBJECT               // Deprecated: use golang.org/x/sys/windows.SE_DS_OBJECT
	SE_DS_OBJECT_ALL           = windows.SE_DS_OBJECT_ALL           // Deprecated: use golang.org/x/sys/windows.SE_DS_OBJECT_ALL
	SE_PROVIDER_DEFINED_OBJECT = windows.SE_PROVIDER_DEFINED_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_PROVIDER_DEFINED_OBJECT
	SE_WMIGUID_OBJECT          = windows.SE_WMIGUID_OBJECT          // Deprecated: use golang.org/x/sys/windows.SE_WMIGUID_OBJECT
	SE_REGISTRY_WOW64_32KEY    = windows.SE_REGISTRY_WOW64_32KEY    // Deprecated: use golang.org/x/sys/windows.SE_REGISTRY_WOW64_32KEY
)

const (
	SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
)

const (
	ContainerAdministratorSidString = "S-1-5-93-2-1"
	ContainerUserSidString          = "S-1-5-93-2-2"
)

var (
	ntuserApiset                  = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
	modadvapi32                   = windows.NewLazySystemDLL("advapi32.dll")
	procGetVersionExW             = modkernel32.NewProc("GetVersionExW")
	procSetNamedSecurityInfo      = modadvapi32.NewProc("SetNamedSecurityInfoW")
	procGetSecurityDescriptorDacl = modadvapi32.NewProc("GetSecurityDescriptorDacl")
)

// OSVersion is a wrapper for Windows version information
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
type OSVersion = osversion.OSVersion

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
// TODO: use golang.org/x/sys/windows.OsVersionInfoEx (needs OSVersionInfoSize to be exported)
type osVersionInfoEx struct {
	OSVersionInfoSize uint32
	MajorVersion      uint32
	MinorVersion      uint32
	BuildNumber       uint32
	PlatformID        uint32
	CSDVersion        [128]uint16
	ServicePackMajor  uint16
	ServicePackMinor  uint16
	SuiteMask         uint16
	ProductType       byte
	Reserve           byte
}

// GetOSVersion gets the operating system version on Windows. Note that
// dockerd.exe must be manifested to get the correct version information.
// Deprecated: use github.com/Microsoft/hcsshim/osversion.Get() instead
func GetOSVersion() OSVersion {
	return osversion.Get()
}

// IsWindowsClient returns true if the SKU is client
func IsWindowsClient() bool {
	osviex := &osVersionInfoEx{OSVersionInfoSize: 284}
	r1, _, err := procGetVersionExW.Call(uintptr(unsafe.Pointer(osviex)))
	if r1 == 0 {
		logrus.Warnf("GetVersionExW failed - assuming server SKU: %v", err)
		return false
	}
	const verNTWorkstation = 0x00000001
	return osviex.ProductType == verNTWorkstation
}

// Unmount is a platform-specific helper function to call
// the unmount syscall. Not supported on Windows
func Unmount(_ string) error {
	return nil
}

// HasWin32KSupport determines whether containers that depend on win32k can
// run on this machine. Win32k is the driver used to implement windowing.
func HasWin32KSupport() bool {
	// For now, check for ntuser API support on the host. In the future, a host
	// may support win32k in containers even if the host does not support ntuser
	// APIs.
	return ntuserApiset.Load() == nil
}

// Deprecated: use golang.org/x/sys/windows.SetNamedSecurityInfo()
func SetNamedSecurityInfo(objectName *uint16, objectType uint32, securityInformation uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *byte, sacl *byte) (result error) {
	r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfo.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0)
	if r0 != 0 {
		result = syscall.Errno(r0)
	}
	return
}

// Deprecated: uses golang.org/x/sys/windows.SecurityDescriptorFromString() and golang.org/x/sys/windows.SECURITY_DESCRIPTOR.DACL()
func GetSecurityDescriptorDacl(securityDescriptor *byte, daclPresent *uint32, dacl **byte, daclDefaulted *uint32) (result error) {
	r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(securityDescriptor)), uintptr(unsafe.Pointer(daclPresent)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclDefaulted)), 0, 0)
	if r1 == 0 {
		if e1 != 0 {
			result = e1
		} else {
			result = syscall.EINVAL
		}
	}
	return
}
<commit_msg>pkg/system: remove deprecated GetOSVersion(), consts, SecurityInfo utils.<commit_after>package system // import "github.com/docker/docker/pkg/system"

import (
	"unsafe"

	"github.com/sirupsen/logrus"
	"golang.org/x/sys/windows"
)

const (
	SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
)

const (
	ContainerAdministratorSidString = "S-1-5-93-2-1"
	ContainerUserSidString          = "S-1-5-93-2-2"
)

var (
	ntuserApiset      = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
	procGetVersionExW = modkernel32.NewProc("GetVersionExW")
)

// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
// TODO: use golang.org/x/sys/windows.OsVersionInfoEx (needs OSVersionInfoSize to be exported)
type osVersionInfoEx struct {
	OSVersionInfoSize uint32
	MajorVersion      uint32
	MinorVersion      uint32
	BuildNumber       uint32
	PlatformID        uint32
	CSDVersion        [128]uint16
	ServicePackMajor  uint16
	ServicePackMinor  uint16
	SuiteMask         uint16
	ProductType       byte
	Reserve           byte
}

// IsWindowsClient returns true if the SKU is client. It returns false on
// Windows server, or if an error occurred when making the GetVersionExW
// syscall.
func IsWindowsClient() bool {
	osviex := &osVersionInfoEx{OSVersionInfoSize: 284}
	r1, _, err := procGetVersionExW.Call(uintptr(unsafe.Pointer(osviex)))
	if r1 == 0 {
		logrus.WithError(err).Warn("GetVersionExW failed - assuming server SKU")
		return false
	}
	// VER_NT_WORKSTATION, see https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
	const verNTWorkstation = 0x00000001 // VER_NT_WORKSTATION
	return osviex.ProductType == verNTWorkstation
}

// Unmount is a platform-specific helper function to call
// the unmount syscall. Not supported on Windows
func Unmount(_ string) error {
	return nil
}

// HasWin32KSupport determines whether containers that depend on win32k can
// run on this machine. Win32k is the driver used to implement windowing.
func HasWin32KSupport() bool {
	// For now, check for ntuser API support on the host. In the future, a host
	// may support win32k in containers even if the host does not support ntuser
	// APIs.
	return ntuserApiset.Load() == nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 podutils

import (
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/integer"
)

// IsPodAvailable returns true if a pod is available; false otherwise.
// Precondition for an available pod is that it must be ready. On top
// of that, there are two cases when a pod can be considered available:
// 1. minReadySeconds == 0, or
// 2. LastTransitionTime (is set) + minReadySeconds < current time
func IsPodAvailable(pod *corev1.Pod, minReadySeconds int32, now metav1.Time) bool {
	if !IsPodReady(pod) {
		return false
	}

	c := getPodReadyCondition(pod.Status)
	minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
	if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
		return true
	}
	return false
}

// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady(pod *corev1.Pod) bool {
	return isPodReadyConditionTrue(pod.Status)
}

// IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
func isPodReadyConditionTrue(status corev1.PodStatus) bool {
	condition := getPodReadyCondition(status)
	return condition != nil && condition.Status == corev1.ConditionTrue
}

// GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func getPodReadyCondition(status corev1.PodStatus) *corev1.PodCondition {
	_, condition := getPodCondition(&status, corev1.PodReady)
	return condition
}

// GetPodCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func getPodCondition(status *corev1.PodStatus, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
	if status == nil {
		return -1, nil
	}
	return getPodConditionFromList(status.Conditions, conditionType)
}

// GetPodConditionFromList extracts the provided condition from the given list of condition and
// returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
func getPodConditionFromList(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
	if conditions == nil {
		return -1, nil
	}
	for i := range conditions {
		if conditions[i].Type == conditionType {
			return i, &conditions[i]
		}
	}
	return -1, nil
}

// ByLogging allows custom sorting of pods so the best one can be picked for getting its logs.
type ByLogging []*corev1.Pod

func (s ByLogging) Len() int      { return len(s) }
func (s ByLogging) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s ByLogging) Less(i, j int) bool {
	// 1. assigned < unassigned
	if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
		return len(s[i].Spec.NodeName) > 0
	}
	// 2. PodRunning < PodUnknown < PodPending
	m := map[corev1.PodPhase]int{corev1.PodRunning: 0, corev1.PodUnknown: 1, corev1.PodPending: 2}
	if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
		return m[s[i].Status.Phase] < m[s[j].Status.Phase]
	}
	// 3. ready < not ready
	if IsPodReady(s[i]) != IsPodReady(s[j]) {
		return IsPodReady(s[i])
	}
	// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
	//       see https://github.com/kubernetes/kubernetes/issues/22065
	// 4. Been ready for more time < less time < empty time
	if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
		return afterOrZero(podReadyTime(s[j]), podReadyTime(s[i]))
	}
	// 5. Pods with containers with higher restart counts < lower restart counts
	if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
		return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
	}
	// 6. older pods < newer pods < empty timestamp pods
	if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
		return afterOrZero(&s[j].CreationTimestamp, &s[i].CreationTimestamp)
	}
	return false
}

// ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete.
type ActivePods []*corev1.Pod

func (s ActivePods) Len() int      { return len(s) }
func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s ActivePods) Less(i, j int) bool {
	// 1. Unassigned < assigned
	// If only one of the pods is unassigned, the unassigned one is smaller
	if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
		return len(s[i].Spec.NodeName) == 0
	}
	// 2. PodPending < PodUnknown < PodRunning
	m := map[corev1.PodPhase]int{corev1.PodPending: 0, corev1.PodUnknown: 1, corev1.PodRunning: 2}
	if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
		return m[s[i].Status.Phase] < m[s[j].Status.Phase]
	}
	// 3. Not ready < ready
	// If only one of the pods is not ready, the not ready one is smaller
	if IsPodReady(s[i]) != IsPodReady(s[j]) {
		return !IsPodReady(s[i])
	}
	// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
	//       see https://github.com/kubernetes/kubernetes/issues/22065
	// 4. Been ready for empty time < less time < more time
	// If both pods are ready, the latest ready one is smaller
	if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
		return afterOrZero(podReadyTime(s[i]), podReadyTime(s[j]))
	}
	// 5. Pods with containers with higher restart counts < lower restart counts
	if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
		return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
	}
	// 6. Empty creation time pods < newer pods < older pods
	if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
		return afterOrZero(&s[i].CreationTimestamp, &s[j].CreationTimestamp)
	}
	return false
}

// afterOrZero checks if time t1 is after time t2; if one of them
// is zero, the zero time is seen as after non-zero time.
func afterOrZero(t1, t2 *metav1.Time) bool {
	if t1.Time.IsZero() || t2.Time.IsZero() {
		return t1.Time.IsZero()
	}
	return t1.After(t2.Time)
}

func podReadyTime(pod *corev1.Pod) *metav1.Time {
	if IsPodReady(pod) {
		for _, c := range pod.Status.Conditions {
			// we only care about pod ready conditions
			if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
				return &c.LastTransitionTime
			}
		}
	}
	return &metav1.Time{}
}

func maxContainerRestarts(pod *corev1.Pod) int {
	maxRestarts := 0
	for _, c := range pod.Status.ContainerStatuses {
		maxRestarts = integer.IntMax(maxRestarts, int(c.RestartCount))
	}
	return maxRestarts
}
<commit_msg>Remove unnecessary traversal of pod.Status.Conditions<commit_after>/*
Copyright 2014 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 podutils

import (
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/integer"
)

// IsPodAvailable returns true if a pod is available; false otherwise.
// Precondition for an available pod is that it must be ready. On top
// of that, there are two cases when a pod can be considered available:
// 1. minReadySeconds == 0, or
// 2. LastTransitionTime (is set) + minReadySeconds < current time
func IsPodAvailable(pod *corev1.Pod, minReadySeconds int32, now metav1.Time) bool {
	if !IsPodReady(pod) {
		return false
	}

	c := getPodReadyCondition(pod.Status)
	minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
	if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
		return true
	}
	return false
}

// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady(pod *corev1.Pod) bool {
	return isPodReadyConditionTrue(pod.Status)
}

// IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
func isPodReadyConditionTrue(status corev1.PodStatus) bool {
	condition := getPodReadyCondition(status)
	return condition != nil && condition.Status == corev1.ConditionTrue
}

// GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func getPodReadyCondition(status corev1.PodStatus) *corev1.PodCondition {
	_, condition := getPodCondition(&status, corev1.PodReady)
	return condition
}

// GetPodCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func getPodCondition(status *corev1.PodStatus, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
	if status == nil {
		return -1, nil
	}
	return getPodConditionFromList(status.Conditions, conditionType)
}

// GetPodConditionFromList extracts the provided condition from the given list of condition and
// returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
func getPodConditionFromList(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) {
	if conditions == nil {
		return -1, nil
	}
	for i := range conditions {
		if conditions[i].Type == conditionType {
			return i, &conditions[i]
		}
	}
	return -1, nil
}

// ByLogging allows custom sorting of pods so the best one can be picked for getting its logs.
type ByLogging []*corev1.Pod

func (s ByLogging) Len() int      { return len(s) }
func (s ByLogging) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s ByLogging) Less(i, j int) bool {
	// 1. assigned < unassigned
	if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
		return len(s[i].Spec.NodeName) > 0
	}
	// 2. PodRunning < PodUnknown < PodPending
	m := map[corev1.PodPhase]int{corev1.PodRunning: 0, corev1.PodUnknown: 1, corev1.PodPending: 2}
	if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
		return m[s[i].Status.Phase] < m[s[j].Status.Phase]
	}
	// 3. ready < not ready
	if IsPodReady(s[i]) != IsPodReady(s[j]) {
		return IsPodReady(s[i])
	}
	// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
	//       see https://github.com/kubernetes/kubernetes/issues/22065
	// 4. Been ready for more time < less time < empty time
	if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
		return afterOrZero(podReadyTime(s[j]), podReadyTime(s[i]))
	}
	// 5. Pods with containers with higher restart counts < lower restart counts
	if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
		return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
	}
	// 6. older pods < newer pods < empty timestamp pods
	if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
		return afterOrZero(&s[j].CreationTimestamp, &s[i].CreationTimestamp)
	}
	return false
}

// ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete.
type ActivePods []*corev1.Pod

func (s ActivePods) Len() int      { return len(s) }
func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s ActivePods) Less(i, j int) bool {
	// 1. Unassigned < assigned
	// If only one of the pods is unassigned, the unassigned one is smaller
	if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
		return len(s[i].Spec.NodeName) == 0
	}
	// 2. PodPending < PodUnknown < PodRunning
	m := map[corev1.PodPhase]int{corev1.PodPending: 0, corev1.PodUnknown: 1, corev1.PodRunning: 2}
	if m[s[i].Status.Phase] != m[s[j].Status.Phase] {
		return m[s[i].Status.Phase] < m[s[j].Status.Phase]
	}
	// 3. Not ready < ready
	// If only one of the pods is not ready, the not ready one is smaller
	if IsPodReady(s[i]) != IsPodReady(s[j]) {
		return !IsPodReady(s[i])
	}
	// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
	//       see https://github.com/kubernetes/kubernetes/issues/22065
	// 4. Been ready for empty time < less time < more time
	// If both pods are ready, the latest ready one is smaller
	if IsPodReady(s[i]) && IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) {
		return afterOrZero(podReadyTime(s[i]), podReadyTime(s[j]))
	}
	// 5. Pods with containers with higher restart counts < lower restart counts
	if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
		return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
	}
	// 6. Empty creation time pods < newer pods < older pods
	if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
		return afterOrZero(&s[i].CreationTimestamp, &s[j].CreationTimestamp)
	}
	return false
}

// afterOrZero checks if time t1 is after time t2; if one of them
// is zero, the zero time is seen as after non-zero time.
func afterOrZero(t1, t2 *metav1.Time) bool {
	if t1.Time.IsZero() || t2.Time.IsZero() {
		return t1.Time.IsZero()
	}
	return t1.After(t2.Time)
}

func podReadyTime(pod *corev1.Pod) *metav1.Time {
	for _, c := range pod.Status.Conditions {
		// we only care about pod ready conditions
		if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
			return &c.LastTransitionTime
		}
	}
	return &metav1.Time{}
}

func maxContainerRestarts(pod *corev1.Pod) int {
	maxRestarts := 0
	for _, c := range pod.Status.ContainerStatuses {
		maxRestarts = integer.IntMax(maxRestarts, int(c.RestartCount))
	}
	return maxRestarts
}
<|endoftext|>


<commit_before>package margopher

import "testing"

func TestGetRandomWord(t *testing.T) {
	if getRandomWord([]string{"1", "2", "3"}) == "" {
		t.Error("getRandomWord: it should return a string element from slice.")
	}
}

func TestIsTerminalWord(t *testing.T) {
	if isTerminalWord("Hey.") == false {
		t.Error("isTerminalWord: it should return true for words ending in period.")
	}
}

func TestReadText(t *testing.T) {
	m := New()
	m.ParseText("I love cats. Cats love tuna.")

	if m.states == nil {
		t.Error("ParseText: it should initialize states.")
	}
}
<commit_msg>Fix failing test `m.ParseText undefined`<commit_after>package margopher

import "testing"

func TestGetRandomWord(t *testing.T) {
	if getRandomWord([]string{"1", "2", "3"}) == "" {
		t.Error("getRandomWord: it should return a string element from slice.")
	}
}

func TestIsTerminalWord(t *testing.T) {
	if isTerminalWord("Hey.") == false {
		t.Error("isTerminalWord: it should return true for words ending in period.")
	}
}

func TestParse(t *testing.T) {
	m := New()
	m.parse("I love cats. Cats love tuna.")

	if m.states == nil {
		t.Error("ParseText: it should initialize states.")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 csi

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"

	"github.com/golang/glog"
	grpctx "golang.org/x/net/context"
	api "k8s.io/api/core/v1"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	kstrings "k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
)

const defaultFSType = "ext4"

//TODO (vladimirvivien) move this in a central loc later
var (
	volDataKey = struct {
		specVolID,
		volHandle,
		driverName,
		nodeName,
		attachmentID string
	}{
		"specVolID",
		"volumeHandle",
		"driverName",
		"nodeName",
		"attachmentID",
	}
)

type csiMountMgr struct {
	k8s          kubernetes.Interface
	csiClient    csiClient
	plugin       *csiPlugin
	driverName   string
	volumeID     string
	specVolumeID string
	readOnly     bool
	spec         *volume.Spec
	pod          *api.Pod
	podUID       types.UID
	options      volume.VolumeOptions
	volumeInfo   map[string]string
	volume.MetricsNil
}

// volume.Volume methods
var _ volume.Volume = &csiMountMgr{}

func (c *csiMountMgr) GetPath() string {
	dir := path.Join(getTargetPath(c.podUID, c.specVolumeID, c.plugin.host), "/mount")
	glog.V(4).Info(log("mounter.GetPath generated [%s]", dir))
	return dir
}

func getTargetPath(uid types.UID, specVolumeID string, host volume.VolumeHost) string {
	specVolID := kstrings.EscapeQualifiedNameForDisk(specVolumeID)
	return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(csiPluginName), specVolID)
}

// volume.Mounter methods
var _ volume.Mounter = &csiMountMgr{}

func (c *csiMountMgr) CanMount() error {
	return nil
}

func (c *csiMountMgr) SetUp(fsGroup *int64) error {
	return c.SetUpAt(c.GetPath(), fsGroup)
}

func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
	glog.V(4).Infof(log("Mounter.SetUpAt(%s)", dir))

	mounted, err := isDirMounted(c.plugin, dir)
	if err != nil {
		glog.Error(log("mounter.SetUpAt failed while checking mount status for dir [%s]", dir))
		return err
	}

	if mounted {
		glog.V(4).Info(log("mounter.SetUpAt skipping mount, dir already mounted [%s]", dir))
		return nil
	}

	csiSource, err := getCSISourceFromSpec(c.spec)
	if err != nil {
		glog.Error(log("mounter.SetupAt failed to get CSI persistent source: %v", err))
		return err
	}

	csi := c.csiClient
	nodeName := string(c.plugin.host.GetNodeName())
	attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName)

	ctx, cancel := grpctx.WithTimeout(grpctx.Background(), csiTimeout)
	defer cancel()
	// Check for STAGE_UNSTAGE_VOLUME set and populate deviceMountPath if so
	deviceMountPath := ""
	stageUnstageSet, err := hasStageUnstageCapability(ctx, csi)
	if err != nil {
		glog.Error(log("mounter.SetUpAt failed to check for STAGE_UNSTAGE_VOLUME capabilty: %v", err))
		return err
	}

	if stageUnstageSet {
		deviceMountPath, err = makeDeviceMountPath(c.plugin, c.spec)
		if err != nil {
			glog.Error(log("mounter.SetUpAt failed to make device mount path: %v", err))
			return err
		}
	}

	// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
	if c.volumeInfo == nil {
		attachment, err := c.k8s.StorageV1beta1().VolumeAttachments().Get(attachID, meta.GetOptions{})
		if err != nil {
			glog.Error(log("mounter.SetupAt failed while getting volume attachment [id=%v]: %v", attachID, err))
			return err
		}

		if attachment == nil {
			glog.Error(log("unable to find VolumeAttachment [id=%s]", attachID))
			return errors.New("no existing VolumeAttachment found")
		}
		c.volumeInfo = attachment.Status.AttachmentMetadata
	}

	attribs := csiSource.VolumeAttributes

	// create target_dir before call to NodePublish
	if err := os.MkdirAll(dir, 0750); err != nil {
		glog.Error(log("mouter.SetUpAt failed to create dir %#v:  %v", dir, err))
		return err
	}
	glog.V(4).Info(log("created target path successfully [%s]", dir))

	// persist volume info data for teardown
	volData := map[string]string{
		volDataKey.specVolID:    c.spec.Name(),
		volDataKey.volHandle:    csiSource.VolumeHandle,
		volDataKey.driverName:   csiSource.Driver,
		volDataKey.nodeName:     nodeName,
		volDataKey.attachmentID: attachID,
	}

	if err := saveVolumeData(c.plugin, c.podUID, c.spec.Name(), volData); err != nil {
		glog.Error(log("mounter.SetUpAt failed to save volume info data: %v", err))
		if err := removeMountDir(c.plugin, dir); err != nil {
			glog.Error(log("mounter.SetUpAt failed to remove mount dir after a saveVolumeData() error [%s]: %v", dir, err))
			return err
		}
		return err
	}

	//TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
	accessMode := api.ReadWriteOnce
	if c.spec.PersistentVolume.Spec.AccessModes != nil {
		accessMode = c.spec.PersistentVolume.Spec.AccessModes[0]
	}

	fsType := csiSource.FSType
	if len(fsType) == 0 {
		fsType = defaultFSType
	}
	nodePublishSecrets := map[string]string{}
	if csiSource.NodePublishSecretRef != nil {
		nodePublishSecrets = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
	}
	err = csi.NodePublishVolume(
		ctx,
		c.volumeID,
		c.readOnly,
		deviceMountPath,
		dir,
		accessMode,
		c.volumeInfo,
		attribs,
		nodePublishSecrets,
		fsType,
	)

	if err != nil {
		glog.Errorf(log("mounter.SetupAt failed: %v", err))
		if err := removeMountDir(c.plugin, dir); err != nil {
			glog.Error(log("mounter.SetuAt failed to remove mount dir after a NodePublish() error [%s]: %v", dir, err))
			return err
		}
		return err
	}

	glog.V(4).Infof(log("mounter.SetUp successfully requested NodePublish [%s]", dir))
	return nil
}

func (c *csiMountMgr) GetAttributes() volume.Attributes {
	return volume.Attributes{
		ReadOnly:        c.readOnly,
		Managed:         !c.readOnly,
		SupportsSELinux: false,
	}
}

// volume.Unmounter methods
var _ volume.Unmounter = &csiMountMgr{}

func (c *csiMountMgr) TearDown() error {
	return c.TearDownAt(c.GetPath())
}
func (c *csiMountMgr) TearDownAt(dir string) error {
	glog.V(4).Infof(log("Unmounter.TearDown(%s)", dir))

	// is dir even mounted ?
	// TODO (vladimirvivien) this check may not work for an emptyDir or local storage
	// see https://github.com/kubernetes/kubernetes/pull/56836#discussion_r155834524
	mounted, err := isDirMounted(c.plugin, dir)
	if err != nil {
		glog.Error(log("unmounter.Teardown failed while checking mount status for dir [%s]: %v", dir, err))
		return err
	}

	if !mounted {
		glog.V(4).Info(log("unmounter.Teardown skipping unmount, dir not mounted [%s]", dir))
		return nil
	}

	if err != nil {
		glog.Error(log("mounter.TearDownAt failed to get CSI persistent source: %v", err))
		return err
	}

	// load volume info from file
	dataDir := path.Dir(dir) // dropoff /mount at end
	data, err := loadVolumeData(dataDir, volDataFileName)
	if err != nil {
		glog.Error(log("unmounter.Teardown failed to load volume data file using dir [%s]: %v", dir, err))
		return err
	}

	volID := data[volDataKey.volHandle]
	driverName := data[volDataKey.driverName]

	if c.csiClient == nil {
		addr := fmt.Sprintf(csiAddrTemplate, driverName)
		client := newCsiDriverClient("unix", addr)
		glog.V(4).Infof(log("unmounter csiClient setup [volume=%v,driver=%v]", volID, driverName))
		c.csiClient = client
	}

	ctx, cancel := grpctx.WithTimeout(grpctx.Background(), csiTimeout)
	defer cancel()

	csi := c.csiClient

	if err := csi.NodeUnpublishVolume(ctx, volID, dir); err != nil {
		glog.Errorf(log("mounter.TearDownAt failed: %v", err))
		return err
	}

	// clean mount point dir
	if err := removeMountDir(c.plugin, dir); err != nil {
		glog.Error(log("mounter.TearDownAt failed to clean mount dir [%s]: %v", dir, err))
		return err
	}
	glog.V(4).Infof(log("mounte.TearDownAt successfully unmounted dir [%s]", dir))

	return nil
}

// saveVolumeData persists parameter data as json file using the location
// generated by /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<specVolId>/volume_data.json
func saveVolumeData(p *csiPlugin, podUID types.UID, specVolID string, data map[string]string) error {
	dir := getTargetPath(podUID, specVolID, p.host)
	dataFilePath := path.Join(dir, volDataFileName)

	file, err := os.Create(dataFilePath)
	if err != nil {
		glog.Error(log("failed to save volume data file %s: %v", dataFilePath, err))
		return err
	}
	defer file.Close()
	if err := json.NewEncoder(file).Encode(data); err != nil {
		glog.Error(log("failed to save volume data file %s: %v", dataFilePath, err))
		return err
	}
	glog.V(4).Info(log("volume data file saved successfully [%s]", dataFilePath))
	return nil
}

// loadVolumeData uses the directory returned by mounter.GetPath with value
// /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<specVolumeId>/mount.
// The function extracts specVolumeID and uses it to load the json data file from dir
// /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<specVolId>/volume_data.json
func loadVolumeData(dir string, fileName string) (map[string]string, error) {
	// remove /mount at the end
	dataFileName := path.Join(dir, fileName)
	glog.V(4).Info(log("loading volume data file [%s]", dataFileName))

	file, err := os.Open(dataFileName)
	if err != nil {
		glog.Error(log("failed to open volume data file [%s]: %v", dataFileName, err))
		return nil, err
	}
	defer file.Close()
	data := map[string]string{}
	if err := json.NewDecoder(file).Decode(&data); err != nil {
		glog.Error(log("failed to parse volume data file [%s]: %v", dataFileName, err))
		return nil, err
	}

	return data, nil
}

// isDirMounted returns the !notMounted result from IsLikelyNotMountPoint check
func isDirMounted(plug *csiPlugin, dir string) (bool, error) {
	mounter := plug.host.GetMounter(plug.GetPluginName())
	notMnt, err := mounter.IsLikelyNotMountPoint(dir)
	if err != nil && !os.IsNotExist(err) {
		glog.Error(log("isDirMounted IsLikelyNotMountPoint test failed for dir [%v]", dir))
		return false, err
	}
	return !notMnt, nil
}

// removeMountDir cleans the mount dir when dir is not mounted and removed the volume data file in dir
func removeMountDir(plug *csiPlugin, mountPath string) error {
	glog.V(4).Info(log("removing mount path [%s]", mountPath))
	if pathExists, pathErr := util.PathExists(mountPath); pathErr != nil {
		glog.Error(log("failed while checking mount path stat [%s]", pathErr))
		return pathErr
	} else if !pathExists {
		glog.Warning(log("skipping mount dir removal, path does not exist [%v]", mountPath))
		return nil
	}

	mounter := plug.host.GetMounter(plug.GetPluginName())
	notMnt, err := mounter.IsLikelyNotMountPoint(mountPath)
	if err != nil {
		glog.Error(log("mount dir removal failed [%s]: %v", mountPath, err))
		return err
	}
	if notMnt {
		glog.V(4).Info(log("dir not mounted, deleting it [%s]", mountPath))
		if err := os.Remove(mountPath); err != nil && !os.IsNotExist(err) {
			glog.Error(log("failed to remove dir [%s]: %v", mountPath, err))
			return err
		}
		// remove volume data file as well
		volPath := path.Dir(mountPath)
		dataFile := path.Join(volPath, volDataFileName)
		glog.V(4).Info(log("also deleting volume info data file [%s]", dataFile))
		if err := os.Remove(dataFile); err != nil && !os.IsNotExist(err) {
			glog.Error(log("failed to delete volume data file [%s]: %v", dataFile, err))
			return err
		}
		// remove volume path
		glog.V(4).Info(log("deleting volume path [%s]", volPath))
		if err := os.Remove(volPath); err != nil && !os.IsNotExist(err) {
			glog.Error(log("failed to delete volume path [%s]: %v", volPath, err))
			return err
		}
	}
	return nil
}
<commit_msg>the err has checked in TearDownAt func/kind bug<commit_after>/*
Copyright 2017 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 csi

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"

	"github.com/golang/glog"
	grpctx "golang.org/x/net/context"
	api "k8s.io/api/core/v1"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	kstrings "k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
)

const defaultFSType = "ext4"

//TODO (vladimirvivien) move this in a central loc later
var (
	volDataKey = struct {
		specVolID,
		volHandle,
		driverName,
		nodeName,
		attachmentID string
	}{
		"specVolID",
		"volumeHandle",
		"driverName",
		"nodeName",
		"attachmentID",
	}
)

type csiMountMgr struct {
	k8s          kubernetes.Interface
	csiClient    csiClient
	plugin       *csiPlugin
	driverName   string
	volumeID     string
	specVolumeID string
	readOnly     bool
	spec         *volume.Spec
	pod          *api.Pod
	podUID       types.UID
	options      volume.VolumeOptions
	volumeInfo   map[string]string
	volume.MetricsNil
}

// volume.Volume methods
var _ volume.Volume = &csiMountMgr{}

func (c *csiMountMgr) GetPath() string {
	dir := path.Join(getTargetPath(c.podUID, c.specVolumeID, c.plugin.host), "/mount")
	glog.V(4).Info(log("mounter.GetPath generated [%s]", dir))
	return dir
}

func getTargetPath(uid types.UID, specVolumeID string, host volume.VolumeHost) string {
	specVolID := kstrings.EscapeQualifiedNameForDisk(specVolumeID)
	return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(csiPluginName), specVolID)
}

// volume.Mounter methods
var _ volume.Mounter = &csiMountMgr{}

func (c *csiMountMgr) CanMount() error {
	return nil
}

func (c *csiMountMgr) SetUp(fsGroup *int64) error {
	return c.SetUpAt(c.GetPath(), fsGroup)
}

func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
	glog.V(4).Infof(log("Mounter.SetUpAt(%s)", dir))

	mounted, err := isDirMounted(c.plugin, dir)
	if err != nil {
		glog.Error(log("mounter.SetUpAt failed while checking mount status for dir [%s]", dir))
		return err
	}

	if mounted {
		glog.V(4).Info(log("mounter.SetUpAt skipping mount, dir already mounted [%s]", dir))
		return nil
	}

	csiSource, err := getCSISourceFromSpec(c.spec)
	if err != nil {
		glog.Error(log("mounter.SetupAt failed to get CSI persistent source: %v", err))
		return err
	}

	csi := c.csiClient
	nodeName := string(c.plugin.host.GetNodeName())
	attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName)

	ctx, cancel := grpctx.WithTimeout(grpctx.Background(), csiTimeout)
	defer cancel()
	// Check for STAGE_UNSTAGE_VOLUME set and populate deviceMountPath if so
	deviceMountPath := ""
	stageUnstageSet, err := hasStageUnstageCapability(ctx, csi)
	if err != nil {
		glog.Error(log("mounter.SetUpAt failed to check for STAGE_UNSTAGE_VOLUME capabilty: %v", err))
		return err
	}

	if stageUnstageSet {
		deviceMountPath, err = makeDeviceMountPath(c.plugin, c.spec)
		if err != nil {
			glog.Error(log("mounter.SetUpAt failed to make device mount path: %v", err))
			return err
		}
	}

	// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
	if c.volumeInfo == nil {
		attachment, err := c.k8s.StorageV1beta1().VolumeAttachments().Get(attachID, meta.GetOptions{})
		if err != nil {
			glog.Error(log("mounter.SetupAt failed while getting volume attachment [id=%v]: %v", attachID, err))
			return err
		}

		if attachment == nil {
			glog.Error(log("unable to find VolumeAttachment [id=%s]", attachID))
			return errors.New("no existing VolumeAttachment found")
		}
		c.volumeInfo = attachment.Status.AttachmentMetadata
	}

	attribs := csiSource.VolumeAttributes

	// create target_dir before call to NodePublish
	if err := os.MkdirAll(dir, 0750); err != nil {
		glog.Error(log("mouter.SetUpAt failed to create dir %#v:  %v", dir, err))
		return err
	}
	glog.V(4).Info(log("created target path successfully [%s]", dir))

	// persist volume info data for teardown
	volData := map[string]string{
		volDataKey.specVolID:    c.spec.Name(),
		volDataKey.volHandle:    csiSource.VolumeHandle,
		volDataKey.driverName:   csiSource.Driver,
		volDataKey.nodeName:     nodeName,
		volDataKey.attachmentID: attachID,
	}

	if err := saveVolumeData(c.plugin, c.podUID, c.spec.Name(), volData); err != nil {
		glog.Error(log("mounter.SetUpAt failed to save volume info data: %v", err))
		if err := removeMountDir(c.plugin, dir); err != nil {
			glog.Error(log("mounter.SetUpAt failed to remove mount dir after a saveVolumeData() error [%s]: %v", dir, err))
			return err
		}
		return err
	}

	//TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
	accessMode := api.ReadWriteOnce
	if c.spec.PersistentVolume.Spec.AccessModes != nil {
		accessMode = c.spec.PersistentVolume.Spec.AccessModes[0]
	}

	fsType := csiSource.FSType
	if len(fsType) == 0 {
		fsType = defaultFSType
	}
	nodePublishSecrets := map[string]string{}
	if csiSource.NodePublishSecretRef != nil {
		nodePublishSecrets = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
	}
	err = csi.NodePublishVolume(
		ctx,
		c.volumeID,
		c.readOnly,
		deviceMountPath,
		dir,
		accessMode,
		c.volumeInfo,
		attribs,
		nodePublishSecrets,
		fsType,
	)

	if err != nil {
		glog.Errorf(log("mounter.SetupAt failed: %v", err))
		if err := removeMountDir(c.plugin, dir); err != nil {
			glog.Error(log("mounter.SetuAt failed to remove mount dir after a NodePublish() error [%s]: %v", dir, err))
			return err
		}
		return err
	}

	glog.V(4).Infof(log("mounter.SetUp successfully requested NodePublish [%s]", dir))
	return nil
}

func (c *csiMountMgr) GetAttributes() volume.Attributes {
	return volume.Attributes{
		ReadOnly:        c.readOnly,
		Managed:         !c.readOnly,
		SupportsSELinux: false,
	}
}

// volume.Unmounter methods
var _ volume.Unmounter = &csiMountMgr{}

func (c *csiMountMgr) TearDown() error {
	return c.TearDownAt(c.GetPath())
}
func (c *csiMountMgr) TearDownAt(dir string) error {
	glog.V(4).Infof(log("Unmounter.TearDown(%s)", dir))

	// is dir even mounted ?
	// TODO (vladimirvivien) this check may not work for an emptyDir or local storage
	// see https://github.com/kubernetes/kubernetes/pull/56836#discussion_r155834524
	mounted, err := isDirMounted(c.plugin, dir)
	if err != nil {
		glog.Error(log("unmounter.Teardown failed while checking mount status for dir [%s]: %v", dir, err))
		return err
	}

	if !mounted {
		glog.V(4).Info(log("unmounter.Teardown skipping unmount, dir not mounted [%s]", dir))
		return nil
	}

	// load volume info from file
	dataDir := path.Dir(dir) // dropoff /mount at end
	data, err := loadVolumeData(dataDir, volDataFileName)
	if err != nil {
		glog.Error(log("unmounter.Teardown failed to load volume data file using dir [%s]: %v", dir, err))
		return err
	}

	volID := data[volDataKey.volHandle]
	driverName := data[volDataKey.driverName]

	if c.csiClient == nil {
		addr := fmt.Sprintf(csiAddrTemplate, driverName)
		client := newCsiDriverClient("unix", addr)
		glog.V(4).Infof(log("unmounter csiClient setup [volume=%v,driver=%v]", volID, driverName))
		c.csiClient = client
	}

	ctx, cancel := grpctx.WithTimeout(grpctx.Background(), csiTimeout)
	defer cancel()

	csi := c.csiClient

	if err := csi.NodeUnpublishVolume(ctx, volID, dir); err != nil {
		glog.Errorf(log("mounter.TearDownAt failed: %v", err))
		return err
	}

	// clean mount point dir
	if err := removeMountDir(c.plugin, dir); err != nil {
		glog.Error(log("mounter.TearDownAt failed to clean mount dir [%s]: %v", dir, err))
		return err
	}
	glog.V(4).Infof(log("mounte.TearDownAt successfully unmounted dir [%s]", dir))

	return nil
}

// saveVolumeData persists parameter data as json file using the location
// generated by /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<specVolId>/volume_data.json
func saveVolumeData(p *csiPlugin, podUID types.UID, specVolID string, data map[string]string) error {
	dir := getTargetPath(podUID, specVolID, p.host)
	dataFilePath := path.Join(dir, volDataFileName)

	file, err := os.Create(dataFilePath)
	if err != nil {
		glog.Error(log("failed to save volume data file %s: %v", dataFilePath, err))
		return err
	}
	defer file.Close()
	if err := json.NewEncoder(file).Encode(data); err != nil {
		glog.Error(log("failed to save volume data file %s: %v", dataFilePath, err))
		return err
	}
	glog.V(4).Info(log("volume data file saved successfully [%s]", dataFilePath))
	return nil
}

// loadVolumeData uses the directory returned by mounter.GetPath with value
// /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<specVolumeId>/mount.
// The function extracts specVolumeID and uses it to load the json data file from dir
// /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<specVolId>/volume_data.json
func loadVolumeData(dir string, fileName string) (map[string]string, error) {
	// remove /mount at the end
	dataFileName := path.Join(dir, fileName)
	glog.V(4).Info(log("loading volume data file [%s]", dataFileName))

	file, err := os.Open(dataFileName)
	if err != nil {
		glog.Error(log("failed to open volume data file [%s]: %v", dataFileName, err))
		return nil, err
	}
	defer file.Close()
	data := map[string]string{}
	if err := json.NewDecoder(file).Decode(&data); err != nil {
		glog.Error(log("failed to parse volume data file [%s]: %v", dataFileName, err))
		return nil, err
	}

	return data, nil
}

// isDirMounted returns the !notMounted result from IsLikelyNotMountPoint check
func isDirMounted(plug *csiPlugin, dir string) (bool, error) {
	mounter := plug.host.GetMounter(plug.GetPluginName())
	notMnt, err := mounter.IsLikelyNotMountPoint(dir)
	if err != nil && !os.IsNotExist(err) {
		glog.Error(log("isDirMounted IsLikelyNotMountPoint test failed for dir [%v]", dir))
		return false, err
	}
	return !notMnt, nil
}

// removeMountDir cleans the mount dir when dir is not mounted and removed the volume data file in dir
func removeMountDir(plug *csiPlugin, mountPath string) error {
	glog.V(4).Info(log("removing mount path [%s]", mountPath))
	if pathExists, pathErr := util.PathExists(mountPath); pathErr != nil {
		glog.Error(log("failed while checking mount path stat [%s]", pathErr))
		return pathErr
	} else if !pathExists {
		glog.Warning(log("skipping mount dir removal, path does not exist [%v]", mountPath))
		return nil
	}

	mounter := plug.host.GetMounter(plug.GetPluginName())
	notMnt, err := mounter.IsLikelyNotMountPoint(mountPath)
	if err != nil {
		glog.Error(log("mount dir removal failed [%s]: %v", mountPath, err))
		return err
	}
	if notMnt {
		glog.V(4).Info(log("dir not mounted, deleting it [%s]", mountPath))
		if err := os.Remove(mountPath); err != nil && !os.IsNotExist(err) {
			glog.Error(log("failed to remove dir [%s]: %v", mountPath, err))
			return err
		}
		// remove volume data file as well
		volPath := path.Dir(mountPath)
		dataFile := path.Join(volPath, volDataFileName)
		glog.V(4).Info(log("also deleting volume info data file [%s]", dataFile))
		if err := os.Remove(dataFile); err != nil && !os.IsNotExist(err) {
			glog.Error(log("failed to delete volume data file [%s]: %v", dataFile, err))
			return err
		}
		// remove volume path
		glog.V(4).Info(log("deleting volume path [%s]", volPath))
		if err := os.Remove(volPath); err != nil && !os.IsNotExist(err) {
			glog.Error(log("failed to delete volume path [%s]: %v", volPath, err))
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package bot

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/uva-its/yaml"
	// MakeDaemon from VividCortex - thanks!
	"github.com/VividCortex/godaemon"
)

var started bool

type BotInfo struct {
	LogFile, PidFile string // Locations for the bots log file and pid file
}

func dirExists(path string) bool {
	if len(path) == 0 {
		return false
	}
	ds, err := os.Stat(path)
	if err != nil {
		return false
	}
	if ds.Mode().IsDir() {
		return true
	}
	return false
}

func Start() {
	botLock.Lock()
	if started {
		botLock.Unlock()
		return
	}
	started = true
	botLock.Unlock()
	var execpath, execdir, installdir, localdir string
	var err error

	// Process command-line flags
	var configDir string
	cusage := "path to the local configuration directory"
	flag.StringVar(&configDir, "config", "", cusage)
	flag.StringVar(&configDir, "c", "", cusage+" (shorthand)")
	var installDir string
	iusage := "path to the local install directory containing default/stock configuration"
	flag.StringVar(&installDir, "install", "", iusage)
	flag.StringVar(&installDir, "i", "", iusage+" (shorthand)")
	var logFile string
	lusage := "path to robot's log file"
	flag.StringVar(&logFile, "log", "", lusage)
	flag.StringVar(&logFile, "l", "", lusage+" (shorthand)")
	var pidFile string
	pusage := "path to robot's pid file"
	flag.StringVar(&pidFile, "pid", "", pusage)
	flag.StringVar(&pidFile, "p", "", pusage+" (shorthand)")
	var daemonize bool
	fusage := "run the robot as a background process"
	flag.BoolVar(&daemonize, "daemonize", false, fusage)
	flag.BoolVar(&daemonize, "d", false, fusage+" (shorthand)")
	flag.Parse()

	// Installdir is where the default config and stock external
	// plugins are.
	execpath, err = godaemon.GetExecutablePath()
	if err == nil {
		execdir, _ = filepath.Abs(filepath.Dir(execpath))
	}
	instSearchPath := []string{
		installDir,
		os.Getenv("GOPHER_INSTALLDIR"),
		"/opt/gopherbot",
		"/usr/local/share/gopherbot",
		"/usr/share/gopherbot",
	}
	gosearchpath := os.Getenv("GOPATH")
	if len(gosearchpath) > 0 {
		for _, gopath := range strings.Split(gosearchpath, ":") {
			instSearchPath = append(instSearchPath, gopath+"/src/github.com/uva-its/gopherbot")
		}
	}
	instSearchPath = append(instSearchPath, execdir)
	for _, spath := range instSearchPath {
		if dirExists(spath) {
			installdir = spath
			break
		}
	}

	// Localdir is where all user-supplied configuration and
	// external plugins are.
	home := os.Getenv("HOME")
	confSearchPath := []string{
		configDir,
		os.Getenv("GOPHER_LOCALDIR"),
		home + "/.gopherbot",
		"/usr/local/etc/gopherbot",
		"/etc/gopherbot",
	}
	for _, spath := range confSearchPath {
		if dirExists(spath) {
			localdir = spath
			break
		}
	}
	if len(localdir) == 0 {
		log.Fatal("Couldn't locate local configuration directory")
	}

	// Read the config just to extract the LogFile PidFile path
	var cf []byte
	if cf, err = ioutil.ReadFile(localdir + "/conf/gopherbot.yaml"); err != nil {
		log.Fatalf("Couldn't read conf/gopherbot.yaml in local configuration directory: %s\n", localdir)
	}
	var bi BotInfo
	if err := yaml.Unmarshal(cf, &bi); err != nil {
		log.Fatalf("Error unmarshalling \"%s\": %v", localdir+"/conf/gopherbot.yaml", err)
	}

	var botLogger *log.Logger
	if daemonize {
		var f *os.File
		if godaemon.Stage() == godaemon.StageParent {
			var (
				lp  string
				err error
			)
			if len(logFile) != 0 {
				lp = logFile
			} else if len(bi.LogFile) != 0 {
				lp = bi.LogFile
			} else {
				lp = "/tmp/gopherbot.log"
			}
			f, err = os.Create(lp)
			if err != nil {
				log.Fatalf("Couldn't create log file: %v", err)
			}
			log.Printf("Backgrounding and logging to: %s\n", lp)
		}
		_, _, err := godaemon.MakeDaemon(&godaemon.DaemonAttr{
			Files:         []**os.File{&f},
			ProgramName:   "gopherbot",
			CaptureOutput: false,
		})
		// Don't double-timestamp if another package is using the default logger
		log.SetFlags(0)
		botLogger = log.New(f, "", log.LstdFlags)
		if err != nil {
			botLogger.Fatalf("Problem daemonizing: %v", err)
		}
		var pf string
		if len(pidFile) != 0 {
			pf = pidFile
		} else if len(bi.PidFile) != 0 {
			pf = bi.PidFile
		}
		if len(pf) != 0 {
			f, err := os.Create(pf)
			if err != nil {
				botLogger.Printf("Couldn't create pid file: %v", err)
			} else {
				pid := os.Getpid()
				fmt.Fprintf(f, "%d", pid)
				botLogger.Printf("Wrote pid (%d) to: %s\n", pid, pf)
				f.Close()
			}
		}
	} else { // run in the foreground, log to stderr
		botLogger = log.New(os.Stderr, "", log.LstdFlags)
	}

	// From here on out we're daemonized if -d was passed
	// Create the 'bot and load configuration, supplying configdir and installdir.
	// When loading configuration, gopherbot first loads default configuration
	// from internal config, then loads from localdir/conf/..., which
	// overrides defaults.
	os.Setenv("GOPHER_INSTALLDIR", installdir)
	os.Setenv("GOPHER_LOCALDIR", localdir)
	err = newBot(localdir, installdir, botLogger)
	if err != nil {
		botLogger.Fatal(fmt.Errorf("Error loading initial configuration: %v", err))
	}
	Log(Info, fmt.Sprintf("Starting up with localdir: %s, and installdir: %s", localdir, installdir))

	var conn Connector

	connectionStarter, ok := connectors[b.protocol]
	if !ok {
		botLogger.Fatal("No connector registered with name:", b.protocol)
	}

	// handler{} is just a placeholder struct for implementing the Handler interface
	h := handler{}
	conn = connectionStarter(h, botLogger)

	// Initialize the robot with a valid connector
	botInit(conn)

	// Start the connector's main loop
	conn.Run()
}
<commit_msg>Fix installdir lookup to check for lib/ subdir; exit 0 when unconfigured<commit_after>package bot

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/uva-its/yaml"
	// MakeDaemon from VividCortex - thanks!
	"github.com/VividCortex/godaemon"
)

var started bool

type BotInfo struct {
	LogFile, PidFile string // Locations for the bots log file and pid file
}

func dirExists(path string) bool {
	if len(path) == 0 {
		return false
	}
	log.Printf("Checking %s\n", path)
	ds, err := os.Stat(path)
	if err != nil {
		return false
	}
	if ds.Mode().IsDir() {
		return true
	}
	return false
}

func Start() {
	botLock.Lock()
	if started {
		botLock.Unlock()
		return
	}
	started = true
	botLock.Unlock()
	var execpath, execdir, installdir, localdir string
	var err error

	// Process command-line flags
	var configDir string
	cusage := "path to the local configuration directory"
	flag.StringVar(&configDir, "config", "", cusage)
	flag.StringVar(&configDir, "c", "", cusage+" (shorthand)")
	var installDir string
	iusage := "path to the local install directory containing default/stock configuration"
	flag.StringVar(&installDir, "install", "", iusage)
	flag.StringVar(&installDir, "i", "", iusage+" (shorthand)")
	var logFile string
	lusage := "path to robot's log file"
	flag.StringVar(&logFile, "log", "", lusage)
	flag.StringVar(&logFile, "l", "", lusage+" (shorthand)")
	var pidFile string
	pusage := "path to robot's pid file"
	flag.StringVar(&pidFile, "pid", "", pusage)
	flag.StringVar(&pidFile, "p", "", pusage+" (shorthand)")
	var daemonize bool
	fusage := "run the robot as a background process"
	flag.BoolVar(&daemonize, "daemonize", false, fusage)
	flag.BoolVar(&daemonize, "d", false, fusage+" (shorthand)")
	flag.Parse()

	// Installdir is where the default config and stock external
	// plugins are.
	execpath, err = godaemon.GetExecutablePath()
	if err == nil {
		execdir, _ = filepath.Abs(filepath.Dir(execpath))
	}
	instSearchPath := []string{
		installDir,
		os.Getenv("GOPHER_INSTALLDIR"),
		"/opt/gopherbot",
		"/usr/local/share/gopherbot",
		"/usr/share/gopherbot",
	}
	gosearchpath := os.Getenv("GOPATH")
	if len(gosearchpath) > 0 {
		for _, gopath := range strings.Split(gosearchpath, ":") {
			instSearchPath = append(instSearchPath, gopath+"/src/github.com/uva-its/gopherbot")
		}
	}
	instSearchPath = append(instSearchPath, execdir)
	for _, spath := range instSearchPath {
		if len(spath) > 0 && dirExists(spath + "/lib") {
			installdir = spath
			break
		}
	}
	if len(installdir) == 0 {
		log.Println("Install directory not found, exiting")
		os.Exit(0)
	}

	// Localdir is where all user-supplied configuration and
	// external plugins are.
	home := os.Getenv("HOME")
	confSearchPath := []string{
		configDir,
		os.Getenv("GOPHER_LOCALDIR"),
		home + "/.gopherbot",
		"/usr/local/etc/gopherbot",
		"/etc/gopherbot",
	}
	for _, spath := range confSearchPath {
		if len(spath) > 0 && dirExists(spath) {
			localdir = spath
			break
		}
	}
	if len(localdir) == 0 {
		log.Println("Couldn't locate local configuration directory, exiting")
		os.Exit(0)
	}

	// Read the config just to extract the LogFile PidFile path
	var cf []byte
	if cf, err = ioutil.ReadFile(localdir + "/conf/gopherbot.yaml"); err != nil {
		log.Fatalf("Couldn't read conf/gopherbot.yaml in local configuration directory: %s\n", localdir)
	}
	var bi BotInfo
	if err := yaml.Unmarshal(cf, &bi); err != nil {
		log.Fatalf("Error unmarshalling \"%s\": %v", localdir+"/conf/gopherbot.yaml", err)
	}

	var botLogger *log.Logger
	if daemonize {
		var f *os.File
		if godaemon.Stage() == godaemon.StageParent {
			var (
				lp  string
				err error
			)
			if len(logFile) != 0 {
				lp = logFile
			} else if len(bi.LogFile) != 0 {
				lp = bi.LogFile
			} else {
				lp = "/tmp/gopherbot.log"
			}
			f, err = os.Create(lp)
			if err != nil {
				log.Fatalf("Couldn't create log file: %v", err)
			}
			log.Printf("Backgrounding and logging to: %s\n", lp)
		}
		_, _, err := godaemon.MakeDaemon(&godaemon.DaemonAttr{
			Files:         []**os.File{&f},
			ProgramName:   "gopherbot",
			CaptureOutput: false,
		})
		// Don't double-timestamp if another package is using the default logger
		log.SetFlags(0)
		botLogger = log.New(f, "", log.LstdFlags)
		if err != nil {
			botLogger.Fatalf("Problem daemonizing: %v", err)
		}
		var pf string
		if len(pidFile) != 0 {
			pf = pidFile
		} else if len(bi.PidFile) != 0 {
			pf = bi.PidFile
		}
		if len(pf) != 0 {
			f, err := os.Create(pf)
			if err != nil {
				botLogger.Printf("Couldn't create pid file: %v", err)
			} else {
				pid := os.Getpid()
				fmt.Fprintf(f, "%d", pid)
				botLogger.Printf("Wrote pid (%d) to: %s\n", pid, pf)
				f.Close()
			}
		}
	} else { // run in the foreground, log to stderr
		botLogger = log.New(os.Stderr, "", log.LstdFlags)
	}

	// From here on out we're daemonized if -d was passed
	// Create the 'bot and load configuration, supplying configdir and installdir.
	// When loading configuration, gopherbot first loads default configuration
	// from internal config, then loads from localdir/conf/..., which
	// overrides defaults.
	os.Setenv("GOPHER_INSTALLDIR", installdir)
	os.Setenv("GOPHER_LOCALDIR", localdir)
	err = newBot(localdir, installdir, botLogger)
	if err != nil {
		botLogger.Fatal(fmt.Errorf("Error loading initial configuration: %v", err))
	}
	Log(Info, fmt.Sprintf("Starting up with localdir: %s, and installdir: %s", localdir, installdir))

	var conn Connector

	connectionStarter, ok := connectors[b.protocol]
	if !ok {
		botLogger.Fatal("No connector registered with name:", b.protocol)
	}

	// handler{} is just a placeholder struct for implementing the Handler interface
	h := handler{}
	conn = connectionStarter(h, botLogger)

	// Initialize the robot with a valid connector
	botInit(conn)

	// Start the connector's main loop
	conn.Run()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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.
//
// Author: Tamir Duberstein (tamird@gmail.com)
// Author: Andrei Matei (andreimatei1@gmail.com)
// Author: Nathan VanBenschoten (nvanbenschoten@gmail.com)

package sqlbase

import (
	"fmt"
	"strings"

	"github.com/cockroachdb/cockroach/sql/parser"
	"github.com/cockroachdb/cockroach/sql/pgwire/pgerror"
	"github.com/cockroachdb/cockroach/util/caller"
)

// Cockroach error extensions:
const (
	// CodeRetriableError signals to the user that the SQL txn entered the
	// RESTART_WAIT state and that a RESTART statement should be issued.
	CodeRetriableError string = "CR000"
	// CodeTransactionCommittedError signals that the SQL txn is in the
	// COMMIT_WAIT state and a COMMIT statement should be issued.
	CodeTransactionCommittedError string = "CR001"
)

// SrcCtx contains contextual information about the source of an error.
type SrcCtx struct {
	File     string
	Line     int
	Function string
}

func makeSrcCtx(depth int) SrcCtx {
	f, l, fun := caller.Lookup(depth + 1)
	return SrcCtx{File: f, Line: l, Function: fun}
}

// ErrorWithPGCode represents errors that carries an error code to the user.
// pgwire recognizes this interfaces and extracts the code.
type ErrorWithPGCode interface {
	error
	Code() string
	SrcContext() SrcCtx
}

var _ ErrorWithPGCode = &ErrNonNullViolation{}
var _ ErrorWithPGCode = &ErrUniquenessConstraintViolation{}
var _ ErrorWithPGCode = &ErrTransactionAborted{}
var _ ErrorWithPGCode = &ErrTransactionCommitted{}
var _ ErrorWithPGCode = &ErrUndefinedDatabase{}
var _ ErrorWithPGCode = &ErrUndefinedTable{}
var _ ErrorWithPGCode = &ErrRetry{}

const (
	txnAbortedMsg = "current transaction is aborted, commands ignored " +
		"until end of transaction block"
	txnCommittedMsg = "current transaction is committed, commands ignored " +
		"until end of transaction block"
	txnRetryMsgPrefix = "restart transaction:"
)

// NewRetryError creates a ErrRetry.
func NewRetryError(cause error) error {
	return &ErrRetry{ctx: makeSrcCtx(1), msg: fmt.Sprintf("%s %v", txnRetryMsgPrefix, cause)}
}

// ErrRetry means that the transaction can be retried.
type ErrRetry struct {
	ctx SrcCtx
	msg string
}

func (e *ErrRetry) Error() string {
	return e.msg
}

// Code implements the ErrorWithPGCode interface.
func (*ErrRetry) Code() string {
	return CodeRetriableError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrRetry) SrcContext() SrcCtx {
	return e.ctx
}

// NewTransactionAbortedError creates a new ErrTransactionAborted.
func NewTransactionAbortedError(customMsg string) error {
	return &ErrTransactionAborted{ctx: makeSrcCtx(1), CustomMsg: customMsg}
}

// ErrTransactionAborted represents an error for trying to run a command in the
// context of transaction that's already aborted.
type ErrTransactionAborted struct {
	ctx       SrcCtx
	CustomMsg string
}

func (e *ErrTransactionAborted) Error() string {
	msg := txnAbortedMsg
	if e.CustomMsg != "" {
		msg += "; " + e.CustomMsg
	}
	return msg
}

// Code implements the ErrorWithPGCode interface.
func (*ErrTransactionAborted) Code() string {
	return pgerror.CodeInFailedSQLTransactionError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrTransactionAborted) SrcContext() SrcCtx {
	return e.ctx
}

// NewTransactionCommittedError creates a new ErrTransactionCommitted.
func NewTransactionCommittedError() error {
	return &ErrTransactionCommitted{ctx: makeSrcCtx(1)}
}

// ErrTransactionCommitted represents an error for trying to run a command in the
// context of transaction that's already committed.
type ErrTransactionCommitted struct {
	ctx SrcCtx
}

func (*ErrTransactionCommitted) Error() string {
	return txnCommittedMsg
}

// Code implements the ErrorWithPGCode interface.
func (*ErrTransactionCommitted) Code() string {
	return CodeTransactionCommittedError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrTransactionCommitted) SrcContext() SrcCtx {
	return e.ctx
}

// NewNonNullViolationError creates a new ErrNonNullViolation.
func NewNonNullViolationError(columnName string) error {
	return &ErrNonNullViolation{ctx: makeSrcCtx(1), columnName: columnName}
}

// ErrNonNullViolation represents a violation of a non-NULL constraint.
type ErrNonNullViolation struct {
	ctx        SrcCtx
	columnName string
}

func (e *ErrNonNullViolation) Error() string {
	return fmt.Sprintf("null value in column %q violates not-null constraint", e.columnName)
}

// Code implements the ErrorWithPGCode interface.
func (*ErrNonNullViolation) Code() string {
	return pgerror.CodeNotNullViolationError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrNonNullViolation) SrcContext() SrcCtx {
	return e.ctx
}

// NewUniquenessConstraintViolationError creates a new
// ErrUniquenessConstrainViolation.
func NewUniquenessConstraintViolationError(
	index *IndexDescriptor, vals []parser.Datum,
) error {
	return &ErrUniquenessConstraintViolation{
		ctx:   makeSrcCtx(1),
		index: index,
		vals:  vals,
	}
}

// ErrUniquenessConstraintViolation represents a violation of a UNIQUE constraint.
type ErrUniquenessConstraintViolation struct {
	ctx   SrcCtx
	index *IndexDescriptor
	vals  []parser.Datum
}

// Code implements the ErrorWithPGCode interface.
func (*ErrUniquenessConstraintViolation) Code() string {
	return pgerror.CodeUniqueViolationError
}

func (e *ErrUniquenessConstraintViolation) Error() string {
	valStrs := make([]string, 0, len(e.vals))
	for _, val := range e.vals {
		valStrs = append(valStrs, val.String())
	}

	return fmt.Sprintf("duplicate key value (%s)=(%s) violates unique constraint %q",
		strings.Join(e.index.ColumnNames, ","),
		strings.Join(valStrs, ","),
		e.index.Name)
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrUniquenessConstraintViolation) SrcContext() SrcCtx {
	return e.ctx
}

// NewUndefinedTableError creates a new ErrUndefinedTable.
func NewUndefinedTableError(name string) error {
	return &ErrUndefinedTable{ctx: makeSrcCtx(1), name: name}
}

// ErrUndefinedTable represents a missing database table.
type ErrUndefinedTable struct {
	ctx  SrcCtx
	name string
}

func (e *ErrUndefinedTable) Error() string {
	return fmt.Sprintf("table %q does not exist", e.name)
}

// Code implements the ErrorWithPGCode interface.
func (*ErrUndefinedTable) Code() string {
	return pgerror.CodeUndefinedTableError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrUndefinedTable) SrcContext() SrcCtx {
	return e.ctx
}

// NewUndefinedDatabaseError creates a new ErrUndefinedDatabase.
func NewUndefinedDatabaseError(name string) error {
	return &ErrUndefinedDatabase{ctx: makeSrcCtx(1), name: name}
}

// ErrUndefinedDatabase represents a missing database error.
type ErrUndefinedDatabase struct {
	ctx  SrcCtx
	name string
}

func (e *ErrUndefinedDatabase) Error() string {
	return fmt.Sprintf("database %q does not exist", e.name)
}

// Code implements the ErrorWithPGCode interface.
func (*ErrUndefinedDatabase) Code() string {
	// Postgres will return an UndefinedTable error on queries that go to a "relation"
	// that does not exist (a query to a non-existent table or database), but will
	// return an InvalidCatalogName error when connecting to a database that does
	// not exist. We've chosen to return this code for all cases where the error cause
	// is a missing database.
	return pgerror.CodeInvalidCatalogNameError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrUndefinedDatabase) SrcContext() SrcCtx {
	return e.ctx
}

// IsIntegrityConstraintError returns true if the error is some kind of SQL
// contraint violation.
func IsIntegrityConstraintError(err error) bool {
	switch err.(type) {
	case *ErrNonNullViolation, *ErrUniquenessConstraintViolation:
		return true
	default:
		return false
	}
}
<commit_msg>sql: Remove Cockroach extension errors<commit_after>// Copyright 2015 The Cockroach 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.
//
// Author: Tamir Duberstein (tamird@gmail.com)
// Author: Andrei Matei (andreimatei1@gmail.com)
// Author: Nathan VanBenschoten (nvanbenschoten@gmail.com)

package sqlbase

import (
	"fmt"
	"strings"

	"github.com/cockroachdb/cockroach/sql/parser"
	"github.com/cockroachdb/cockroach/sql/pgwire/pgerror"
	"github.com/cockroachdb/cockroach/util/caller"
)

// SrcCtx contains contextual information about the source of an error.
type SrcCtx struct {
	File     string
	Line     int
	Function string
}

func makeSrcCtx(depth int) SrcCtx {
	f, l, fun := caller.Lookup(depth + 1)
	return SrcCtx{File: f, Line: l, Function: fun}
}

// ErrorWithPGCode represents errors that carries an error code to the user.
// pgwire recognizes this interfaces and extracts the code.
type ErrorWithPGCode interface {
	error
	Code() string
	SrcContext() SrcCtx
}

var _ ErrorWithPGCode = &ErrNonNullViolation{}
var _ ErrorWithPGCode = &ErrUniquenessConstraintViolation{}
var _ ErrorWithPGCode = &ErrTransactionAborted{}
var _ ErrorWithPGCode = &ErrTransactionCommitted{}
var _ ErrorWithPGCode = &ErrUndefinedDatabase{}
var _ ErrorWithPGCode = &ErrUndefinedTable{}
var _ ErrorWithPGCode = &ErrRetry{}

const (
	txnAbortedMsg = "current transaction is aborted, commands ignored " +
		"until end of transaction block"
	txnCommittedMsg = "current transaction is committed, commands ignored " +
		"until end of transaction block"
	txnRetryMsgPrefix = "restart transaction:"
)

// NewRetryError creates a ErrRetry.
func NewRetryError(cause error) error {
	return &ErrRetry{ctx: makeSrcCtx(1), msg: fmt.Sprintf("%s %v", txnRetryMsgPrefix, cause)}
}

// ErrRetry means that the transaction can be retried. It signals to the user
// that the SQL txn entered the RESTART_WAIT state after a serialization error,
// and that a ROLLBACK TO SAVEPOINT COCKROACH_RESTART statement should be issued.
type ErrRetry struct {
	ctx SrcCtx
	msg string
}

func (e *ErrRetry) Error() string {
	return e.msg
}

// Code implements the ErrorWithPGCode interface.
func (*ErrRetry) Code() string {
	return pgerror.CodeSerializationFailureError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrRetry) SrcContext() SrcCtx {
	return e.ctx
}

// NewTransactionAbortedError creates a new ErrTransactionAborted.
func NewTransactionAbortedError(customMsg string) error {
	return &ErrTransactionAborted{ctx: makeSrcCtx(1), CustomMsg: customMsg}
}

// ErrTransactionAborted represents an error for trying to run a command in the
// context of transaction that's already aborted.
type ErrTransactionAborted struct {
	ctx       SrcCtx
	CustomMsg string
}

func (e *ErrTransactionAborted) Error() string {
	msg := txnAbortedMsg
	if e.CustomMsg != "" {
		msg += "; " + e.CustomMsg
	}
	return msg
}

// Code implements the ErrorWithPGCode interface.
func (*ErrTransactionAborted) Code() string {
	return pgerror.CodeInFailedSQLTransactionError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrTransactionAborted) SrcContext() SrcCtx {
	return e.ctx
}

// NewTransactionCommittedError creates a new ErrTransactionCommitted.
func NewTransactionCommittedError() error {
	return &ErrTransactionCommitted{ctx: makeSrcCtx(1)}
}

// ErrTransactionCommitted signals that the SQL txn is in the COMMIT_WAIT state
// and that only a COMMIT statement will be accepted.
type ErrTransactionCommitted struct {
	ctx SrcCtx
}

func (*ErrTransactionCommitted) Error() string {
	return txnCommittedMsg
}

// Code implements the ErrorWithPGCode interface.
func (*ErrTransactionCommitted) Code() string {
	return pgerror.CodeInvalidTransactionStateError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrTransactionCommitted) SrcContext() SrcCtx {
	return e.ctx
}

// NewNonNullViolationError creates a new ErrNonNullViolation.
func NewNonNullViolationError(columnName string) error {
	return &ErrNonNullViolation{ctx: makeSrcCtx(1), columnName: columnName}
}

// ErrNonNullViolation represents a violation of a non-NULL constraint.
type ErrNonNullViolation struct {
	ctx        SrcCtx
	columnName string
}

func (e *ErrNonNullViolation) Error() string {
	return fmt.Sprintf("null value in column %q violates not-null constraint", e.columnName)
}

// Code implements the ErrorWithPGCode interface.
func (*ErrNonNullViolation) Code() string {
	return pgerror.CodeNotNullViolationError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrNonNullViolation) SrcContext() SrcCtx {
	return e.ctx
}

// NewUniquenessConstraintViolationError creates a new
// ErrUniquenessConstrainViolation.
func NewUniquenessConstraintViolationError(
	index *IndexDescriptor, vals []parser.Datum,
) error {
	return &ErrUniquenessConstraintViolation{
		ctx:   makeSrcCtx(1),
		index: index,
		vals:  vals,
	}
}

// ErrUniquenessConstraintViolation represents a violation of a UNIQUE constraint.
type ErrUniquenessConstraintViolation struct {
	ctx   SrcCtx
	index *IndexDescriptor
	vals  []parser.Datum
}

// Code implements the ErrorWithPGCode interface.
func (*ErrUniquenessConstraintViolation) Code() string {
	return pgerror.CodeUniqueViolationError
}

func (e *ErrUniquenessConstraintViolation) Error() string {
	valStrs := make([]string, 0, len(e.vals))
	for _, val := range e.vals {
		valStrs = append(valStrs, val.String())
	}

	return fmt.Sprintf("duplicate key value (%s)=(%s) violates unique constraint %q",
		strings.Join(e.index.ColumnNames, ","),
		strings.Join(valStrs, ","),
		e.index.Name)
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrUniquenessConstraintViolation) SrcContext() SrcCtx {
	return e.ctx
}

// NewUndefinedTableError creates a new ErrUndefinedTable.
func NewUndefinedTableError(name string) error {
	return &ErrUndefinedTable{ctx: makeSrcCtx(1), name: name}
}

// ErrUndefinedTable represents a missing database table.
type ErrUndefinedTable struct {
	ctx  SrcCtx
	name string
}

func (e *ErrUndefinedTable) Error() string {
	return fmt.Sprintf("table %q does not exist", e.name)
}

// Code implements the ErrorWithPGCode interface.
func (*ErrUndefinedTable) Code() string {
	return pgerror.CodeUndefinedTableError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrUndefinedTable) SrcContext() SrcCtx {
	return e.ctx
}

// NewUndefinedDatabaseError creates a new ErrUndefinedDatabase.
func NewUndefinedDatabaseError(name string) error {
	return &ErrUndefinedDatabase{ctx: makeSrcCtx(1), name: name}
}

// ErrUndefinedDatabase represents a missing database error.
type ErrUndefinedDatabase struct {
	ctx  SrcCtx
	name string
}

func (e *ErrUndefinedDatabase) Error() string {
	return fmt.Sprintf("database %q does not exist", e.name)
}

// Code implements the ErrorWithPGCode interface.
func (*ErrUndefinedDatabase) Code() string {
	// Postgres will return an UndefinedTable error on queries that go to a "relation"
	// that does not exist (a query to a non-existent table or database), but will
	// return an InvalidCatalogName error when connecting to a database that does
	// not exist. We've chosen to return this code for all cases where the error cause
	// is a missing database.
	return pgerror.CodeInvalidCatalogNameError
}

// SrcContext implements the ErrorWithPGCode interface.
func (e *ErrUndefinedDatabase) SrcContext() SrcCtx {
	return e.ctx
}

// IsIntegrityConstraintError returns true if the error is some kind of SQL
// contraint violation.
func IsIntegrityConstraintError(err error) bool {
	switch err.(type) {
	case *ErrNonNullViolation, *ErrUniquenessConstraintViolation:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package matchers

import "github.com/h2non/filetype/matchers/isobmff"

var (
	TypeJpeg     = newType("jpg", "image/jpeg")
	TypeJpeg2000 = newType("jp2", "image/jp2")
	TypePng      = newType("png", "image/png")
	TypeGif      = newType("gif", "image/gif")
	TypeWebp     = newType("webp", "image/webp")
	TypeCR2      = newType("cr2", "image/x-canon-cr2")
	TypeTiff     = newType("tif", "image/tiff")
	TypeBmp      = newType("bmp", "image/bmp")
	TypeJxr      = newType("jxr", "image/vnd.ms-photo")
	TypePsd      = newType("psd", "image/vnd.adobe.photoshop")
	TypeIco      = newType("ico", "image/x-icon")
	TypeHeic     = newType("heic", "image/heic")
)

var Image = Map{
	TypeJpeg:     Jpeg,
	TypeJpeg2000: Jpeg2000,
	TypePng:      Png,
	TypeGif:      Gif,
	TypeWebp:     Webp,
	TypeCR2:      CR2,
	TypeTiff:     Tiff,
	TypeBmp:      Bmp,
	TypeJxr:      Jxr,
	TypePsd:      Psd,
	TypeIco:      Ico,
	TypeHeic:     Heic,
}

func Jpeg(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0xFF &&
		buf[1] == 0xD8 &&
		buf[2] == 0xFF
}

func Jpeg2000(buf []byte) bool {
	return len(buf) > 12 &&
		buf[0] == 0x0 &&
		buf[1] == 0x0 &&
		buf[2] == 0x0 &&
		buf[3] == 0xC &&
		buf[4] == 0x6A &&
		buf[5] == 0x50 &&
		buf[6] == 0x20 &&
		buf[7] == 0x20 &&
		buf[8] == 0xD &&
		buf[9] == 0xA &&
		buf[10] == 0x87 &&
		buf[11] == 0xA &&
		buf[12] == 0x0
}

func Png(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x89 && buf[1] == 0x50 &&
		buf[2] == 0x4E && buf[3] == 0x47
}

func Gif(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46
}

func Webp(buf []byte) bool {
	return len(buf) > 11 &&
		buf[8] == 0x57 && buf[9] == 0x45 &&
		buf[10] == 0x42 && buf[11] == 0x50
}

func CR2(buf []byte) bool {
	return len(buf) > 9 &&
		((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
			(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) &&
		buf[8] == 0x43 && buf[9] == 0x52
}

func Tiff(buf []byte) bool {
	return len(buf) > 3 &&
		((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
			(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A))
}

func Bmp(buf []byte) bool {
	return len(buf) > 1 &&
		buf[0] == 0x42 &&
		buf[1] == 0x4D
}

func Jxr(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x49 &&
		buf[1] == 0x49 &&
		buf[2] == 0xBC
}

func Psd(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x38 && buf[1] == 0x42 &&
		buf[2] == 0x50 && buf[3] == 0x53
}

func Ico(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x00 && buf[1] == 0x00 &&
		buf[2] == 0x01 && buf[3] == 0x00
}

func Heic(buf []byte) bool {
	if !isobmff.IsISOBMFF(buf) {
		return false
	}

	majorBrand, _, compatibleBrands := isobmff.GetFtyp(buf)
	if majorBrand == "heic" {
		return true
	}

	if majorBrand == "mif1" || majorBrand == "msf1" {
		for _, compatibleBrand := range compatibleBrands {
			if compatibleBrand == "heic" {
				return true
			}
		}
	}

	return false
}
<commit_msg>refactor(images): heic -> heif<commit_after>package matchers

import "github.com/h2non/filetype/matchers/isobmff"

var (
	TypeJpeg     = newType("jpg", "image/jpeg")
	TypeJpeg2000 = newType("jp2", "image/jp2")
	TypePng      = newType("png", "image/png")
	TypeGif      = newType("gif", "image/gif")
	TypeWebp     = newType("webp", "image/webp")
	TypeCR2      = newType("cr2", "image/x-canon-cr2")
	TypeTiff     = newType("tif", "image/tiff")
	TypeBmp      = newType("bmp", "image/bmp")
	TypeJxr      = newType("jxr", "image/vnd.ms-photo")
	TypePsd      = newType("psd", "image/vnd.adobe.photoshop")
	TypeIco      = newType("ico", "image/x-icon")
	TypeHeif     = newType("heif", "image/heif")
)

var Image = Map{
	TypeJpeg:     Jpeg,
	TypeJpeg2000: Jpeg2000,
	TypePng:      Png,
	TypeGif:      Gif,
	TypeWebp:     Webp,
	TypeCR2:      CR2,
	TypeTiff:     Tiff,
	TypeBmp:      Bmp,
	TypeJxr:      Jxr,
	TypePsd:      Psd,
	TypeIco:      Ico,
	TypeHeif:     Heif,
}

func Jpeg(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0xFF &&
		buf[1] == 0xD8 &&
		buf[2] == 0xFF
}

func Jpeg2000(buf []byte) bool {
	return len(buf) > 12 &&
		buf[0] == 0x0 &&
		buf[1] == 0x0 &&
		buf[2] == 0x0 &&
		buf[3] == 0xC &&
		buf[4] == 0x6A &&
		buf[5] == 0x50 &&
		buf[6] == 0x20 &&
		buf[7] == 0x20 &&
		buf[8] == 0xD &&
		buf[9] == 0xA &&
		buf[10] == 0x87 &&
		buf[11] == 0xA &&
		buf[12] == 0x0
}

func Png(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x89 && buf[1] == 0x50 &&
		buf[2] == 0x4E && buf[3] == 0x47
}

func Gif(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46
}

func Webp(buf []byte) bool {
	return len(buf) > 11 &&
		buf[8] == 0x57 && buf[9] == 0x45 &&
		buf[10] == 0x42 && buf[11] == 0x50
}

func CR2(buf []byte) bool {
	return len(buf) > 9 &&
		((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
			(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) &&
		buf[8] == 0x43 && buf[9] == 0x52
}

func Tiff(buf []byte) bool {
	return len(buf) > 3 &&
		((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
			(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A))
}

func Bmp(buf []byte) bool {
	return len(buf) > 1 &&
		buf[0] == 0x42 &&
		buf[1] == 0x4D
}

func Jxr(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x49 &&
		buf[1] == 0x49 &&
		buf[2] == 0xBC
}

func Psd(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x38 && buf[1] == 0x42 &&
		buf[2] == 0x50 && buf[3] == 0x53
}

func Ico(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x00 && buf[1] == 0x00 &&
		buf[2] == 0x01 && buf[3] == 0x00
}

func Heif(buf []byte) bool {
	if !isobmff.IsISOBMFF(buf) {
		return false
	}

	majorBrand, _, compatibleBrands := isobmff.GetFtyp(buf)
	if majorBrand == "heic" {
		return true
	}

	if majorBrand == "mif1" || majorBrand == "msf1" {
		for _, compatibleBrand := range compatibleBrands {
			if compatibleBrand == "heic" {
				return true
			}
		}
	}

	return false
}
<|endoftext|>


<commit_before>// jwt.go
// JSON Web Tokens for Go

package jwt

import (
  "os"
  "bytes"
  "json"
  "encoding/base64"

  "hash"
  "crypto/hmac"
  "crypto/sha256"
  "crypto/sha512"
)

var separator []byte = []byte{'.'}

var (
  SecretError = os.NewError("Signature verification failed")
)

func base64url_encode(b []byte) []byte {
  encoded := []byte(base64.URLEncoding.EncodeToString(b))
  var equalIndex = bytes.Index(encoded, []byte{'='})
  if equalIndex > -1 {
    encoded = encoded[:equalIndex]
  }
  return encoded
}

func base64url_decode(b []byte) ([]byte, os.Error) {
  if len(b)%4 != 0 {
    b = append(b, bytes.Repeat([]byte{'='}, 4-(len(b)%4))...)
  }
  decoded, err := base64.URLEncoding.DecodeString(string(b))
  if err != nil {
    return nil, err
  }
  return decoded, nil
}

func getHash(algorithm string) (func() hash.Hash, os.Error) {
  switch algorithm {
  case "HS256":
    return sha256.New, nil
  case "HS384":
    return sha512.New384, nil
  case "HS512":
    return sha512.New, nil
  }
  return nil, os.NewError("Algorithm not supported")
}

func Encode(jwt interface{}, key []byte, algorithm string) ([]byte, os.Error) {
  shaFunc, err := getHash(algorithm)
  if err != nil {
    return []byte{}, err
  }
  sha := hmac.New(shaFunc, key)

  segments := [3][]byte{}

  header, err := json.Marshal(
    map[string]interface{}{
      "typ": "JWT",
      "alg": algorithm,
    })
  if err != nil {
    return []byte{}, err
  }
  segments[0] = base64url_encode(header)

  claims, err := json.Marshal(jwt)
  if err != nil {
    return []byte{}, err
  }
  segments[1] = base64url_encode(claims)

  sha.Write(bytes.Join(segments[:2], separator))
  segments[2] = base64url_encode(sha.Sum())

  return bytes.Join(segments[:], separator), nil
}

func Decode(encoded []byte, claims interface{}, key []byte) os.Error {
  segments := bytes.Split(encoded, separator)

  // segments is currently slices make copies so functions like
  // base64url_decode will not overwrite later portions
  for k, v := range segments {
    newBytes := make([]byte, len(v))
    copy(newBytes, v)
    segments[k] = newBytes
  }

  if len(segments) != 3 {
    return os.NewError("Incorrect segment count")
  }

  var header map[string]interface{} = make(map[string]interface{})
  headerBase64, err := base64url_decode(segments[0])
  if err != nil {
    return err
  }
  err = json.Unmarshal(headerBase64, &header)
  if err != nil {
    return err
  }

  algorithm, ok := header["alg"].(string)
  var sha hash.Hash
  if ok {
    shaFunc, err := getHash(algorithm)
    if err != nil {
      return err
    }
    sha = hmac.New(shaFunc, key)
  } else {
    return os.NewError("Algorithm not supported")
  }

  claimsBase64, err := base64url_decode(segments[1])
  if err != nil {
    return err
  }
  err = json.Unmarshal(claimsBase64, claims)
  if err != nil {
    return err
  }

  sha.Write(bytes.Join(segments[:2], separator))
  signature := base64url_encode(sha.Sum())
  if bytes.Compare(signature, segments[2]) != 0 {
    return SecretError
  }
  return nil
}
<commit_msg>updated for go 1.0.1<commit_after>// jwt.go
// JSON Web Tokens for Go

package jwt

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"

	"crypto/hmac"
	"crypto/sha256"
	"crypto/sha512"
	"hash"
)

var separator []byte = []byte{'.'}

var (
	SecretError = errors.New("Signature verification failed")
)

func base64url_encode(b []byte) []byte {
	encoded := []byte(base64.URLEncoding.EncodeToString(b))
	var equalIndex = bytes.Index(encoded, []byte{'='})
	if equalIndex > -1 {
		encoded = encoded[:equalIndex]
	}
	return encoded
}

func base64url_decode(b []byte) ([]byte, error) {
	if len(b)%4 != 0 {
		b = append(b, bytes.Repeat([]byte{'='}, 4-(len(b)%4))...)
	}
	decoded, err := base64.URLEncoding.DecodeString(string(b))
	if err != nil {
		return nil, err
	}
	return decoded, nil
}

func getHash(algorithm string) (func() hash.Hash, error) {
	switch algorithm {
	case "HS256":
		return sha256.New, nil
	case "HS384":
		return sha512.New384, nil
	case "HS512":
		return sha512.New, nil
	}
	return nil, errors.New("Algorithm not supported")
}

func Encode(jwt interface{}, key []byte, algorithm string) ([]byte, error) {
	shaFunc, err := getHash(algorithm)
	if err != nil {
		return []byte{}, err
	}
	sha := hmac.New(shaFunc, key)

	segments := [3][]byte{}

	header, err := json.Marshal(
		map[string]interface{}{
			"typ": "JWT",
			"alg": algorithm,
		})
	if err != nil {
		return []byte{}, err
	}
	segments[0] = base64url_encode(header)

	claims, err := json.Marshal(jwt)
	if err != nil {
		return []byte{}, err
	}
	segments[1] = base64url_encode(claims)

	sha.Write(bytes.Join(segments[:2], separator))
	segments[2] = base64url_encode(sha.Sum(nil))

	return bytes.Join(segments[:], separator), nil
}

func Decode(encoded []byte, claims interface{}, key []byte) error {
	segments := bytes.Split(encoded, separator)

	// segments is currently slices make copies so functions like
	// base64url_decode will not overwrite later portions
	for k, v := range segments {
		newBytes := make([]byte, len(v))
		copy(newBytes, v)
		segments[k] = newBytes
	}

	if len(segments) != 3 {
		return errors.New("Incorrect segment count")
	}

	var header map[string]interface{} = make(map[string]interface{})
	headerBase64, err := base64url_decode(segments[0])
	if err != nil {
		return err
	}
	err = json.Unmarshal(headerBase64, &header)
	if err != nil {
		return err
	}

	algorithm, ok := header["alg"].(string)
	var sha hash.Hash
	if ok {
		shaFunc, err := getHash(algorithm)
		if err != nil {
			return err
		}
		sha = hmac.New(shaFunc, key)
	} else {
		return errors.New("Algorithm not supported")
	}

	claimsBase64, err := base64url_decode(segments[1])
	if err != nil {
		return err
	}
	err = json.Unmarshal(claimsBase64, claims)
	if err != nil {
		return err
	}

	sha.Write(bytes.Join(segments[:2], separator))
	signature := base64url_encode(sha.Sum(nil))
	if bytes.Compare(signature, segments[2]) != 0 {
		return SecretError
	}
	return nil
}
<|endoftext|>