<commit_before>package watcher

import (
	"context"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
	"github.com/rancher/norman/controller"
	"github.com/rancher/rancher/pkg/controllers/user/alert/common"
	"github.com/rancher/rancher/pkg/controllers/user/alert/manager"
	"github.com/rancher/rancher/pkg/controllers/user/workload"
	"github.com/rancher/rancher/pkg/ticker"
	v1 "github.com/rancher/types/apis/core/v1"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	kerrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
)

type PodWatcher struct {
	podLister               v1.PodLister
	alertManager            *manager.AlertManager
	projectAlertPolicies    v3.ProjectAlertRuleInterface
	projectAlertGroupLister v3.ProjectAlertRuleLister
	clusterName             string
	podRestartTrack         sync.Map
	clusterLister           v3.ClusterLister
	projectLister           v3.ProjectLister
	workloadFetcher         workloadFetcher
}

type restartTrack struct {
	Count int32
	Time  time.Time
}

func StartPodWatcher(ctx context.Context, cluster *config.UserContext, manager *manager.AlertManager) {
	projectAlertPolicies := cluster.Management.Management.ProjectAlertRules("")
	workloadFetcher := workloadFetcher{
		workloadController: workload.NewWorkloadController(ctx, cluster.UserOnlyContext(), nil),
	}

	podWatcher := &PodWatcher{
		podLister:               cluster.Core.Pods("").Controller().Lister(),
		projectAlertPolicies:    projectAlertPolicies,
		projectAlertGroupLister: projectAlertPolicies.Controller().Lister(),
		alertManager:            manager,
		clusterName:             cluster.ClusterName,
		podRestartTrack:         sync.Map{},
		clusterLister:           cluster.Management.Management.Clusters("").Controller().Lister(),
		projectLister:           cluster.Management.Management.Projects(cluster.ClusterName).Controller().Lister(),
		workloadFetcher:         workloadFetcher,
	}

	projectAlertLifecycle := &ProjectAlertLifecycle{
		podWatcher: podWatcher,
	}
	projectAlertPolicies.AddClusterScopedLifecycle(ctx, "pod-target-alert-watcher", cluster.ClusterName, projectAlertLifecycle)

	go podWatcher.watch(ctx, syncInterval)
}

func (w *PodWatcher) watch(ctx context.Context, interval time.Duration) {
	for range ticker.Context(ctx, interval) {
		err := w.watchRule()
		if err != nil {
			logrus.Infof("Failed to watch pod, error: %v", err)
		}
	}
}

type ProjectAlertLifecycle struct {
	podWatcher *PodWatcher
}

func (l *ProjectAlertLifecycle) Create(obj *v3.ProjectAlertRule) (runtime.Object, error) {
	l.podWatcher.podRestartTrack.Store(obj.Namespace+":"+obj.Name, make([]restartTrack, 0))
	return obj, nil
}

func (l *ProjectAlertLifecycle) Updated(obj *v3.ProjectAlertRule) (runtime.Object, error) {
	return obj, nil
}

func (l *ProjectAlertLifecycle) Remove(obj *v3.ProjectAlertRule) (runtime.Object, error) {
	l.podWatcher.podRestartTrack.Delete(obj.Namespace + ":" + obj.Name)
	return obj, nil
}

func (w *PodWatcher) watchRule() error {
	if w.alertManager.IsDeploy == false {
		return nil
	}

	projectAlerts, err := w.projectAlertGroupLister.List("", labels.NewSelector())
	if err != nil {
		return err
	}

	pAlerts := []*v3.ProjectAlertRule{}
	for _, alert := range projectAlerts {
		if controller.ObjectInCluster(w.clusterName, alert) {
			pAlerts = append(pAlerts, alert)
		}
	}

	for _, alert := range pAlerts {
		if alert.Status.AlertState == "inactive" || alert.Spec.PodRule == nil {
			continue
		}

		parts := strings.Split(alert.Spec.PodRule.PodName, ":")
		if len(parts) < 2 {
			//TODO: for invalid format pod
			if err = w.projectAlertPolicies.DeleteNamespaced(alert.Namespace, alert.Name, &metav1.DeleteOptions{}); err != nil {
				return err
			}
			continue
		}

		ns := parts[0]
		podID := parts[1]
		newPod, err := w.podLister.Get(ns, podID)
		if err != nil {
			//TODO: what to do when pod not found
			if kerrors.IsNotFound(err) || newPod == nil {
				if err = w.projectAlertPolicies.DeleteNamespaced(alert.Namespace, alert.Name, &metav1.DeleteOptions{}); err != nil {
					return err
				}
			}
			logrus.Debugf("Failed to get pod %s: %v", podID, err)

			continue
		}

		switch alert.Spec.PodRule.Condition {
		case "notrunning":
			w.checkPodRunning(newPod, alert)
		case "notscheduled":
			w.checkPodScheduled(newPod, alert)
		case "restarts":
			w.checkPodRestarts(newPod, alert)
		}
	}

	return nil
}

func (w *PodWatcher) checkPodRestarts(pod *corev1.Pod, alert *v3.ProjectAlertRule) {

	for _, containerStatus := range pod.Status.ContainerStatuses {
		if containerStatus.State.Running == nil {
			curCount := containerStatus.RestartCount
			preCount := w.getRestartTimeFromTrack(alert, curCount)

			if curCount-preCount >= int32(alert.Spec.PodRule.RestartTimes) {
				ruleID := common.GetRuleID(alert.Spec.GroupName, alert.Name)

				details := ""
				if containerStatus.State.Waiting != nil {
					details = containerStatus.State.Waiting.Message
				}

				clusterDisplayName := common.GetClusterDisplayName(w.clusterName, w.clusterLister)
				projectDisplayName := common.GetProjectDisplayName(alert.Spec.ProjectName, w.projectLister)

				data := map[string]string{}
				data["rule_id"] = ruleID
				data["group_id"] = alert.Spec.GroupName
				data["alert_name"] = alert.Spec.DisplayName
				data["alert_type"] = "podRestarts"
				data["severity"] = alert.Spec.Severity
				data["cluster_name"] = clusterDisplayName
				data["project_name"] = projectDisplayName
				data["namespace"] = pod.Namespace
				data["pod_name"] = pod.Name
				data["container_name"] = containerStatus.Name
				data["restart_times"] = strconv.Itoa(alert.Spec.PodRule.RestartTimes)
				data["restart_interval"] = strconv.Itoa(alert.Spec.PodRule.RestartIntervalSeconds)

				if details != "" {
					data["logs"] = details
				}

				workloadName, err := w.getWorkloadInfo(pod)
				if err != nil {
					logrus.Warnf("Failed to get workload info for %s:%s %v", pod.Namespace, pod.Name, err)
				}
				if workloadName != "" {
					data["workload_name"] = workloadName
				}

				if err := w.alertManager.SendAlert(data); err != nil {
					logrus.Debugf("Error occurred while getting pod %s: %v", alert.Spec.PodRule.PodName, err)
				}
			}

			return
		}
	}

}

func (w *PodWatcher) getRestartTimeFromTrack(alert *v3.ProjectAlertRule, curCount int32) int32 {
	name := alert.Name
	namespace := alert.Namespace

	obj, ok := w.podRestartTrack.Load(namespace + ":" + name)
	if !ok {
		return curCount
	}
	tracks := obj.([]restartTrack)

	now := time.Now()

	if len(tracks) == 0 {
		tracks = append(tracks, restartTrack{Count: curCount, Time: now})
		w.podRestartTrack.Store(namespace+":"+name, tracks)
		return curCount
	}

	for i, track := range tracks {
		if now.Sub(track.Time).Seconds() < float64(alert.Spec.PodRule.RestartIntervalSeconds) {
			tracks = tracks[i:]
			tracks = append(tracks, restartTrack{Count: curCount, Time: now})
			w.podRestartTrack.Store(namespace+":"+name, tracks)
			return track.Count
		}
	}

	w.podRestartTrack.Store(namespace+":"+name, []restartTrack{})
	return curCount
}

func (w *PodWatcher) checkPodRunning(pod *corev1.Pod, alert *v3.ProjectAlertRule) {
	if !w.checkPodScheduled(pod, alert) {
		return
	}

	for _, containerStatus := range pod.Status.ContainerStatuses {
		if containerStatus.State.Running == nil {
			ruleID := common.GetRuleID(alert.Spec.GroupName, alert.Name)

			//TODO: need to consider all the cases
			details := ""
			if containerStatus.State.Waiting != nil {
				details = containerStatus.State.Waiting.Message
			}

			if containerStatus.State.Terminated != nil {
				details = containerStatus.State.Terminated.Message
			}

			clusterDisplayName := common.GetClusterDisplayName(w.clusterName, w.clusterLister)
			projectDisplayName := common.GetProjectDisplayName(alert.Spec.ProjectName, w.projectLister)

			data := map[string]string{}
			data["rule_id"] = ruleID
			data["group_id"] = alert.Spec.GroupName
			data["alert_name"] = alert.Spec.DisplayName
			data["alert_type"] = "podNotRunning"
			data["severity"] = alert.Spec.Severity
			data["cluster_name"] = clusterDisplayName
			data["namespace"] = pod.Namespace
			data["project_name"] = projectDisplayName
			data["pod_name"] = pod.Name
			data["container_name"] = containerStatus.Name

			if details != "" {
				data["logs"] = details
			}

			workloadName, err := w.getWorkloadInfo(pod)
			if err != nil {
				logrus.Warnf("Failed to get workload info for %s:%s %v", pod.Namespace, pod.Name, err)
			}
			if workloadName != "" {
				data["workload_name"] = workloadName
			}

			if err := w.alertManager.SendAlert(data); err != nil {
				logrus.Debugf("Error occurred while send alert %s: %v", alert.Spec.PodRule.PodName, err)
			}
			return
		}
	}
}

func (w *PodWatcher) checkPodScheduled(pod *corev1.Pod, alert *v3.ProjectAlertRule) bool {

	for _, condition := range pod.Status.Conditions {
		if condition.Type == corev1.PodScheduled && condition.Status == corev1.ConditionFalse {
			ruleID := common.GetRuleID(alert.Spec.GroupName, alert.Name)
			details := condition.Message

			clusterDisplayName := common.GetClusterDisplayName(w.clusterName, w.clusterLister)
			projectDisplayName := common.GetProjectDisplayName(alert.Spec.ProjectName, w.projectLister)

			data := map[string]string{}
			data["rule_id"] = ruleID
			data["group_id"] = alert.Spec.GroupName
			data["alert_type"] = "podNotScheduled"
			data["alert_name"] = alert.Spec.DisplayName
			data["severity"] = alert.Spec.Severity
			data["cluster_name"] = clusterDisplayName
			data["namespace"] = pod.Namespace
			data["project_name"] = projectDisplayName
			data["pod_name"] = pod.Name

			if details != "" {
				data["logs"] = details
			}

			workloadName, err := w.getWorkloadInfo(pod)
			if err != nil {
				logrus.Warnf("Failed to get workload info for %s:%s %v", pod.Namespace, pod.Name, err)
			}
			if workloadName != "" {
				data["workload_name"] = workloadName
			}

			if err := w.alertManager.SendAlert(data); err != nil {
				logrus.Debugf("Error occurred while getting pod %s: %v", alert.Spec.PodRule.PodName, err)
			}
			return false
		}
	}

	return true

}

func (w *PodWatcher) getWorkloadInfo(pod *corev1.Pod) (string, error) {
	if len(pod.OwnerReferences) == 0 {
		return pod.Name, nil
	}
	ownerRef := pod.OwnerReferences[0]
	workloadName, err := w.workloadFetcher.getWorkloadName(pod.Namespace, ownerRef.Name, ownerRef.Kind)
	if err != nil {
		return "", errors.Wrap(err, "Failed to get workload info for alert")
	}
	return workloadName, nil
}
<commit_msg>Fix pod restart not fire alerting after server restarted<commit_after>package watcher

import (
	"context"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
	"github.com/rancher/norman/controller"
	"github.com/rancher/rancher/pkg/controllers/user/alert/common"
	"github.com/rancher/rancher/pkg/controllers/user/alert/manager"
	"github.com/rancher/rancher/pkg/controllers/user/workload"
	"github.com/rancher/rancher/pkg/ticker"
	v1 "github.com/rancher/types/apis/core/v1"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	kerrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
)

type PodWatcher struct {
	podLister               v1.PodLister
	alertManager            *manager.AlertManager
	projectAlertPolicies    v3.ProjectAlertRuleInterface
	projectAlertGroupLister v3.ProjectAlertRuleLister
	clusterName             string
	podRestartTrack         sync.Map
	clusterLister           v3.ClusterLister
	projectLister           v3.ProjectLister
	workloadFetcher         workloadFetcher
}

type restartTrack struct {
	Count int32
	Time  time.Time
}

func StartPodWatcher(ctx context.Context, cluster *config.UserContext, manager *manager.AlertManager) {
	projectAlertPolicies := cluster.Management.Management.ProjectAlertRules("")
	workloadFetcher := workloadFetcher{
		workloadController: workload.NewWorkloadController(ctx, cluster.UserOnlyContext(), nil),
	}

	podWatcher := &PodWatcher{
		podLister:               cluster.Core.Pods("").Controller().Lister(),
		projectAlertPolicies:    projectAlertPolicies,
		projectAlertGroupLister: projectAlertPolicies.Controller().Lister(),
		alertManager:            manager,
		clusterName:             cluster.ClusterName,
		podRestartTrack:         sync.Map{},
		clusterLister:           cluster.Management.Management.Clusters("").Controller().Lister(),
		projectLister:           cluster.Management.Management.Projects(cluster.ClusterName).Controller().Lister(),
		workloadFetcher:         workloadFetcher,
	}

	projectAlertLifecycle := &ProjectAlertLifecycle{
		podWatcher: podWatcher,
	}
	projectAlertPolicies.AddClusterScopedLifecycle(ctx, "pod-target-alert-watcher", cluster.ClusterName, projectAlertLifecycle)

	go podWatcher.watch(ctx, syncInterval)
}

func (w *PodWatcher) watch(ctx context.Context, interval time.Duration) {
	for range ticker.Context(ctx, interval) {
		err := w.watchRule()
		if err != nil {
			logrus.Infof("Failed to watch pod, error: %v", err)
		}
	}
}

type ProjectAlertLifecycle struct {
	podWatcher *PodWatcher
}

func (l *ProjectAlertLifecycle) Create(obj *v3.ProjectAlertRule) (runtime.Object, error) {
	return obj, nil
}

func (l *ProjectAlertLifecycle) Updated(obj *v3.ProjectAlertRule) (runtime.Object, error) {
	return obj, nil
}

func (l *ProjectAlertLifecycle) Remove(obj *v3.ProjectAlertRule) (runtime.Object, error) {
	l.podWatcher.podRestartTrack.Delete(obj.Namespace + ":" + obj.Name)
	return obj, nil
}

func (w *PodWatcher) watchRule() error {
	if w.alertManager.IsDeploy == false {
		return nil
	}

	projectAlerts, err := w.projectAlertGroupLister.List("", labels.NewSelector())
	if err != nil {
		return err
	}

	pAlerts := []*v3.ProjectAlertRule{}
	for _, alert := range projectAlerts {
		if controller.ObjectInCluster(w.clusterName, alert) {
			pAlerts = append(pAlerts, alert)
		}
	}

	for _, alert := range pAlerts {
		if alert.Status.AlertState == "inactive" || alert.Spec.PodRule == nil {
			continue
		}

		parts := strings.Split(alert.Spec.PodRule.PodName, ":")
		if len(parts) < 2 {
			//TODO: for invalid format pod
			if err = w.projectAlertPolicies.DeleteNamespaced(alert.Namespace, alert.Name, &metav1.DeleteOptions{}); err != nil {
				return err
			}
			continue
		}

		ns := parts[0]
		podID := parts[1]
		newPod, err := w.podLister.Get(ns, podID)
		if err != nil {
			//TODO: what to do when pod not found
			if kerrors.IsNotFound(err) || newPod == nil {
				if err = w.projectAlertPolicies.DeleteNamespaced(alert.Namespace, alert.Name, &metav1.DeleteOptions{}); err != nil {
					return err
				}
			}
			logrus.Debugf("Failed to get pod %s: %v", podID, err)

			continue
		}

		switch alert.Spec.PodRule.Condition {
		case "notrunning":
			w.checkPodRunning(newPod, alert)
		case "notscheduled":
			w.checkPodScheduled(newPod, alert)
		case "restarts":
			w.checkPodRestarts(newPod, alert)
		}
	}

	return nil
}

func (w *PodWatcher) checkPodRestarts(pod *corev1.Pod, alert *v3.ProjectAlertRule) {

	for _, containerStatus := range pod.Status.ContainerStatuses {
		curCount := containerStatus.RestartCount
		preCount := w.getRestartTimeFromTrack(alert, curCount)

		if curCount-preCount >= int32(alert.Spec.PodRule.RestartTimes) {
			ruleID := common.GetRuleID(alert.Spec.GroupName, alert.Name)

			details := ""
			if containerStatus.State.Waiting != nil {
				details = containerStatus.State.Waiting.Message
			}

			clusterDisplayName := common.GetClusterDisplayName(w.clusterName, w.clusterLister)
			projectDisplayName := common.GetProjectDisplayName(alert.Spec.ProjectName, w.projectLister)

			data := map[string]string{}
			data["rule_id"] = ruleID
			data["group_id"] = alert.Spec.GroupName
			data["alert_name"] = alert.Spec.DisplayName
			data["alert_type"] = "podRestarts"
			data["severity"] = alert.Spec.Severity
			data["cluster_name"] = clusterDisplayName
			data["project_name"] = projectDisplayName
			data["namespace"] = pod.Namespace
			data["pod_name"] = pod.Name
			data["container_name"] = containerStatus.Name
			data["restart_times"] = strconv.Itoa(alert.Spec.PodRule.RestartTimes)
			data["restart_interval"] = strconv.Itoa(alert.Spec.PodRule.RestartIntervalSeconds)

			if details != "" {
				data["logs"] = details
			}

			workloadName, err := w.getWorkloadInfo(pod)
			if err != nil {
				logrus.Warnf("Failed to get workload info for %s:%s %v", pod.Namespace, pod.Name, err)
			}
			if workloadName != "" {
				data["workload_name"] = workloadName
			}

			if err := w.alertManager.SendAlert(data); err != nil {
				logrus.Debugf("Error occurred while getting pod %s: %v", alert.Spec.PodRule.PodName, err)
			}
		}

		return
	}

}

func (w *PodWatcher) getRestartTimeFromTrack(alert *v3.ProjectAlertRule, curCount int32) int32 {
	name := alert.Name
	namespace := alert.Namespace
	now := time.Now()
	currentRestartTrack := restartTrack{Count: curCount, Time: now}
	currentRestartTrackArr := []restartTrack{currentRestartTrack}

	obj, loaded := w.podRestartTrack.LoadOrStore(namespace+":"+name, currentRestartTrackArr)
	if loaded {
		tracks := obj.([]restartTrack)
		for i, track := range tracks {
			if now.Sub(track.Time).Seconds() < float64(alert.Spec.PodRule.RestartIntervalSeconds) {
				tracks = tracks[i:]
				tracks = append(tracks, currentRestartTrack)
				w.podRestartTrack.Store(namespace+":"+name, tracks)
				return track.Count
			}
		}
	}

	return curCount
}

func (w *PodWatcher) checkPodRunning(pod *corev1.Pod, alert *v3.ProjectAlertRule) {
	if !w.checkPodScheduled(pod, alert) {
		return
	}

	for _, containerStatus := range pod.Status.ContainerStatuses {
		if containerStatus.State.Running == nil {
			ruleID := common.GetRuleID(alert.Spec.GroupName, alert.Name)

			//TODO: need to consider all the cases
			details := ""
			if containerStatus.State.Waiting != nil {
				details = containerStatus.State.Waiting.Message
			}

			if containerStatus.State.Terminated != nil {
				details = containerStatus.State.Terminated.Message
			}

			clusterDisplayName := common.GetClusterDisplayName(w.clusterName, w.clusterLister)
			projectDisplayName := common.GetProjectDisplayName(alert.Spec.ProjectName, w.projectLister)

			data := map[string]string{}
			data["rule_id"] = ruleID
			data["group_id"] = alert.Spec.GroupName
			data["alert_name"] = alert.Spec.DisplayName
			data["alert_type"] = "podNotRunning"
			data["severity"] = alert.Spec.Severity
			data["cluster_name"] = clusterDisplayName
			data["namespace"] = pod.Namespace
			data["project_name"] = projectDisplayName
			data["pod_name"] = pod.Name
			data["container_name"] = containerStatus.Name

			if details != "" {
				data["logs"] = details
			}

			workloadName, err := w.getWorkloadInfo(pod)
			if err != nil {
				logrus.Warnf("Failed to get workload info for %s:%s %v", pod.Namespace, pod.Name, err)
			}
			if workloadName != "" {
				data["workload_name"] = workloadName
			}

			if err := w.alertManager.SendAlert(data); err != nil {
				logrus.Debugf("Error occurred while send alert %s: %v", alert.Spec.PodRule.PodName, err)
			}
			return
		}
	}
}

func (w *PodWatcher) checkPodScheduled(pod *corev1.Pod, alert *v3.ProjectAlertRule) bool {

	for _, condition := range pod.Status.Conditions {
		if condition.Type == corev1.PodScheduled && condition.Status == corev1.ConditionFalse {
			ruleID := common.GetRuleID(alert.Spec.GroupName, alert.Name)
			details := condition.Message

			clusterDisplayName := common.GetClusterDisplayName(w.clusterName, w.clusterLister)
			projectDisplayName := common.GetProjectDisplayName(alert.Spec.ProjectName, w.projectLister)

			data := map[string]string{}
			data["rule_id"] = ruleID
			data["group_id"] = alert.Spec.GroupName
			data["alert_type"] = "podNotScheduled"
			data["alert_name"] = alert.Spec.DisplayName
			data["severity"] = alert.Spec.Severity
			data["cluster_name"] = clusterDisplayName
			data["namespace"] = pod.Namespace
			data["project_name"] = projectDisplayName
			data["pod_name"] = pod.Name

			if details != "" {
				data["logs"] = details
			}

			workloadName, err := w.getWorkloadInfo(pod)
			if err != nil {
				logrus.Warnf("Failed to get workload info for %s:%s %v", pod.Namespace, pod.Name, err)
			}
			if workloadName != "" {
				data["workload_name"] = workloadName
			}

			if err := w.alertManager.SendAlert(data); err != nil {
				logrus.Debugf("Error occurred while getting pod %s: %v", alert.Spec.PodRule.PodName, err)
			}
			return false
		}
	}

	return true

}

func (w *PodWatcher) getWorkloadInfo(pod *corev1.Pod) (string, error) {
	if len(pod.OwnerReferences) == 0 {
		return pod.Name, nil
	}
	ownerRef := pod.OwnerReferences[0]
	workloadName, err := w.workloadFetcher.getWorkloadName(pod.Namespace, ownerRef.Name, ownerRef.Kind)
	if err != nil {
		return "", errors.Wrap(err, "Failed to get workload info for alert")
	}
	return workloadName, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 color

import (
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
)

// IsTerminal will check if the specified output stream is a terminal. This can be changed
// for testing to an arbitrary method.
var IsTerminal = isTerminal

// Color can be used to format text using ANSI escape codes so it can be printed to
// the terminal in color.
type Color int

var (
	// LightRed can format text to be displayed to the terminal in light red, using ANSI escape codes.
	LightRed = Color(91)
	// LightGreen can format text to be displayed to the terminal in light green, using ANSI escape codes.
	LightGreen = Color(92)
	// LightYellow can format text to be displayed to the terminal in light yellow, using ANSI escape codes.
	LightYellow = Color(93)
	// LightBlue can format text to be displayed to the terminal in light blue, using ANSI escape codes.
	LightBlue = Color(94)
	// LightPurple can format text to be displayed to the terminal in light purple, using ANSI escape codes.
	LightPurple = Color(95)
	// Red can format text to be displayed to the terminal in red, using ANSI escape codes.
	Red = Color(31)
	// Green can format text to be displayed to the terminal in green, using ANSI escape codes.
	Green = Color(32)
	// Yellow can format text to be displayed to the terminal in yellow, using ANSI escape codes.
	Yellow = Color(33)
	// Blue can format text to be displayed to the terminal in blue, using ANSI escape codes.
	Blue = Color(34)
	// Purple can format text to be displayed to the terminal in purple, using ANSI escape codes.
	Purple = Color(35)
	// Cyan can format text to be displayed to the terminal in cyan, using ANSI escape codes.
	Cyan = Color(36)
	// White can format text to be displayed to the terminal in white, using ANSI escape codes.
	White = Color(37)
	// None uses ANSI escape codes to reset all formatting.
	None = Color(0)

	// Default default output color for output from Skaffold to the user
	Default = Blue
)

// Fprint wraps the operands in c's ANSI escape codes, and outputs the result to
// out. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func (c Color) Fprint(out io.Writer, a ...interface{}) (n int, err error) {
	if IsTerminal(out) {
		return fmt.Fprintf(out, "\033[%dm%s\033[0m", c, fmt.Sprint(a...))
	}
	return fmt.Fprint(out, a...)
}

// Fprintln wraps the operands in c's ANSI escape codes, and outputs the result to
// out, followed by a newline. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func (c Color) Fprintln(out io.Writer, a ...interface{}) (n int, err error) {
	if IsTerminal(out) {
		return fmt.Fprintf(out, "\033[%dm%s\033[0m\n", c, strings.TrimSuffix(fmt.Sprintln(a...), "\n"))
	}
	return fmt.Fprintln(out, a...)
}

// Fprintf applies formats according to the format specifier (and the optional interfaces provided),
// wraps the result in c's ANSI escape codes, and outputs the result to
// out, followed by a newline. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func (c Color) Fprintf(out io.Writer, format string, a ...interface{}) (n int, err error) {
	if IsTerminal(out) {
		return fmt.Fprintf(out, "\033[%dm%s\033[0m", c, fmt.Sprintf(format, a...))
	}
	return fmt.Fprintf(out, format, a...)
}

// ColoredWriteCloser forces printing with colors to an io.WriteCloser.
type ColoredWriteCloser struct {
	io.WriteCloser
}

// ColoredWriter forces printing with colors to an io.Writer.
type ColoredWriter struct {
	io.Writer
}

// OverwriteDefault overwrites default color
func OverwriteDefault(color Color) {
	Default = color
}

// This implementation comes from logrus (https://github.com/sirupsen/logrus/blob/master/terminal_check_notappengine.go),
// unfortunately logrus doesn't expose a public interface we can use to call it.
func isTerminal(w io.Writer) bool {
	if _, ok := w.(ColoredWriteCloser); ok {
		return true
	}
	if _, ok := w.(ColoredWriter); ok {
		return true
	}

	switch v := w.(type) {
	case *os.File:
		return terminal.IsTerminal(int(v.Fd()))
	default:
		return false
	}
}
<commit_msg>Remove dead code<commit_after>/*
Copyright 2019 The Skaffold 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 color

import (
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
)

// IsTerminal will check if the specified output stream is a terminal. This can be changed
// for testing to an arbitrary method.
var IsTerminal = isTerminal

// Color can be used to format text using ANSI escape codes so it can be printed to
// the terminal in color.
type Color int

var (
	// LightRed can format text to be displayed to the terminal in light red, using ANSI escape codes.
	LightRed = Color(91)
	// LightGreen can format text to be displayed to the terminal in light green, using ANSI escape codes.
	LightGreen = Color(92)
	// LightYellow can format text to be displayed to the terminal in light yellow, using ANSI escape codes.
	LightYellow = Color(93)
	// LightBlue can format text to be displayed to the terminal in light blue, using ANSI escape codes.
	LightBlue = Color(94)
	// LightPurple can format text to be displayed to the terminal in light purple, using ANSI escape codes.
	LightPurple = Color(95)
	// Red can format text to be displayed to the terminal in red, using ANSI escape codes.
	Red = Color(31)
	// Green can format text to be displayed to the terminal in green, using ANSI escape codes.
	Green = Color(32)
	// Yellow can format text to be displayed to the terminal in yellow, using ANSI escape codes.
	Yellow = Color(33)
	// Blue can format text to be displayed to the terminal in blue, using ANSI escape codes.
	Blue = Color(34)
	// Purple can format text to be displayed to the terminal in purple, using ANSI escape codes.
	Purple = Color(35)
	// Cyan can format text to be displayed to the terminal in cyan, using ANSI escape codes.
	Cyan = Color(36)
	// White can format text to be displayed to the terminal in white, using ANSI escape codes.
	White = Color(37)
	// None uses ANSI escape codes to reset all formatting.
	None = Color(0)

	// Default default output color for output from Skaffold to the user
	Default = Blue
)

// Fprint wraps the operands in c's ANSI escape codes, and outputs the result to
// out. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func (c Color) Fprint(out io.Writer, a ...interface{}) (n int, err error) {
	if IsTerminal(out) {
		return fmt.Fprintf(out, "\033[%dm%s\033[0m", c, fmt.Sprint(a...))
	}
	return fmt.Fprint(out, a...)
}

// Fprintln wraps the operands in c's ANSI escape codes, and outputs the result to
// out, followed by a newline. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func (c Color) Fprintln(out io.Writer, a ...interface{}) (n int, err error) {
	if IsTerminal(out) {
		return fmt.Fprintf(out, "\033[%dm%s\033[0m\n", c, strings.TrimSuffix(fmt.Sprintln(a...), "\n"))
	}
	return fmt.Fprintln(out, a...)
}

// Fprintf applies formats according to the format specifier (and the optional interfaces provided),
// wraps the result in c's ANSI escape codes, and outputs the result to
// out, followed by a newline. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func (c Color) Fprintf(out io.Writer, format string, a ...interface{}) (n int, err error) {
	if IsTerminal(out) {
		return fmt.Fprintf(out, "\033[%dm%s\033[0m", c, fmt.Sprintf(format, a...))
	}
	return fmt.Fprintf(out, format, a...)
}

// ColoredWriteCloser forces printing with colors to an io.WriteCloser.
type ColoredWriteCloser struct {
	io.WriteCloser
}

// OverwriteDefault overwrites default color
func OverwriteDefault(color Color) {
	Default = color
}

// This implementation comes from logrus (https://github.com/sirupsen/logrus/blob/master/terminal_check_notappengine.go),
// unfortunately logrus doesn't expose a public interface we can use to call it.
func isTerminal(w io.Writer) bool {
	if _, ok := w.(ColoredWriteCloser); ok {
		return true
	}

	switch v := w.(type) {
	case *os.File:
		return terminal.IsTerminal(int(v.Fd()))
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package gsocket

import (
	"io"
	"log"
	"net"
	"sync"
)

// Connection 代表一个连接会话
type Connection struct {
	conn       net.Conn
	sendBuffer chan []byte
	terminated bool
}

// newConnection 生成一个新的Session
func newConnection(conn net.Conn) (c *Connection) {
	c = &Connection{
		conn:       conn,
		sendBuffer: make(chan []byte, 10),
		terminated: false,
	}

	return c
}

// RemoteAddr 返回客户端的地址和端口
func (c *Connection) RemoteAddr() string {
	return c.conn.RemoteAddr().String()
}

// Close 关闭连接
func (c *Connection) Close() {
	c.terminated = true
	close(c.sendBuffer)
	c.conn.Close()
}

func (c *Connection) recvThread(wg *sync.WaitGroup, handler tcpEventHandler) {
	defer wg.Done()
	buffer := make([]byte, 4096)
	for {
		n, err := c.conn.Read(buffer)
		if err != nil {
			if err != io.EOF {
				if handler.handlerError != nil {
					handler.handlerError(c, err)
				}

				break
			}

			if handler.handlerDisconnect != nil {
				handler.handlerDisconnect(c)
			}
			break
		}

		//session.RecvedPackets = append(session.RecvedPackets, buffer[:n]...)
		if handler.handlerRecv != nil {
			handler.handlerRecv(c, buffer[:n])
		}
	}

	if c.terminated == false {
		c.Close()
	}
	log.Printf("session %s recvThread Exit", c.RemoteAddr())
}

func (c *Connection) sendThread(wg *sync.WaitGroup) {
	defer wg.Done()

	for {
		packet, ok := <-c.sendBuffer
		if !ok {
			// 意味着道通已经空了，并且已被关闭
			break
		}
		_, err := c.conn.Write(packet)
		if err != nil {
			break
		}
	}

	log.Printf("session %s sendThread Exit", c.RemoteAddr())
}

// Send 发送数据
func (c *Connection) Send(data []byte) {
	c.sendBuffer <- data
}
<commit_msg>recvThread加打印消息<commit_after>package gsocket

import (
	"io"
	"log"
	"net"
	"sync"
)

// Connection 代表一个连接会话
type Connection struct {
	conn       net.Conn
	sendBuffer chan []byte
	terminated bool
}

// newConnection 生成一个新的Session
func newConnection(conn net.Conn) (c *Connection) {
	c = &Connection{
		conn:       conn,
		sendBuffer: make(chan []byte, 10),
		terminated: false,
	}

	return c
}

// RemoteAddr 返回客户端的地址和端口
func (c *Connection) RemoteAddr() string {
	return c.conn.RemoteAddr().String()
}

// LocalAddr 返回本机地址和端口
func (c *Connection) LocalAddr() string {
	return c.conn.LocalAddr().String()
}

// Close 关闭连接
func (c *Connection) Close() {
	c.terminated = true
	close(c.sendBuffer)
	c.conn.Close()
}

func (c *Connection) recvThread(wg *sync.WaitGroup, handler tcpEventHandler) {
	defer wg.Done()
	buffer := make([]byte, 4096)
	for {
		n, err := c.conn.Read(buffer)
		if err != nil {
			if err != io.EOF {
				if handler.handlerError != nil {
					log.Printf("handler error: %s\n", c.LocalAddr())
					handler.handlerError(c, err)
				}
				log.Println("handler read break 1")
				break
			}

			if handler.handlerDisconnect != nil {
				handler.handlerDisconnect(c)
			}
			log.Println("handler read break 2")
			break
		}

		//session.RecvedPackets = append(session.RecvedPackets, buffer[:n]...)
		if handler.handlerRecv != nil {
			handler.handlerRecv(c, buffer[:n])
		}
	}

	if c.terminated == false {
		c.Close()
	}
	log.Printf("session %s recvThread Exit", c.RemoteAddr())
}

func (c *Connection) sendThread(wg *sync.WaitGroup) {
	defer wg.Done()

	for {
		packet, ok := <-c.sendBuffer
		if !ok {
			// 意味着道通已经空了，并且已被关闭
			break
		}
		_, err := c.conn.Write(packet)
		if err != nil {
			break
		}
	}

	log.Printf("session %s sendThread Exit", c.RemoteAddr())
}

// Send 发送数据
func (c *Connection) Send(data []byte) {
	c.sendBuffer <- data
}
<|endoftext|>


<commit_before>package plugins

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go-connections/sockets"
	"github.com/docker/go-connections/tlsconfig"
)

const (
	versionMimetype = "application/vnd.docker.plugins.v1.2+json"
	defaultTimeOut  = 30
)

// NewClient creates a new plugin client (http).
func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
	tr := &http.Transport{}

	c, err := tlsconfig.Client(tlsConfig)
	if err != nil {
		return nil, err
	}
	tr.TLSClientConfig = c

	protoAndAddr := strings.Split(addr, "://")
	if err := sockets.ConfigureTransport(tr, protoAndAddr[0], protoAndAddr[1]); err != nil {
		return nil, err
	}

	scheme := protoAndAddr[0]
	if scheme != "https" {
		scheme = "http"
	}
	return &Client{&http.Client{Transport: tr}, scheme, protoAndAddr[1]}, nil
}

// Client represents a plugin client.
type Client struct {
	http   *http.Client // http client to use
	scheme string       // scheme protocol of the plugin
	addr   string       // http address of the plugin
}

// Call calls the specified method with the specified arguments for the plugin.
// It will retry for 30 seconds if a failure occurs when calling.
func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
	var buf bytes.Buffer
	if args != nil {
		if err := json.NewEncoder(&buf).Encode(args); err != nil {
			return err
		}
	}
	body, err := c.callWithRetry(serviceMethod, &buf, true)
	if err != nil {
		return err
	}
	defer body.Close()
	if ret != nil {
		if err := json.NewDecoder(body).Decode(&ret); err != nil {
			logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
			return err
		}
	}
	return nil
}

// Stream calls the specified method with the specified arguments for the plugin and returns the response body
func (c *Client) Stream(serviceMethod string, args interface{}) (io.ReadCloser, error) {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(args); err != nil {
		return nil, err
	}
	return c.callWithRetry(serviceMethod, &buf, true)
}

// SendFile calls the specified method, and passes through the IO stream
func (c *Client) SendFile(serviceMethod string, data io.Reader, ret interface{}) error {
	body, err := c.callWithRetry(serviceMethod, data, true)
	if err != nil {
		return err
	}
	if err := json.NewDecoder(body).Decode(&ret); err != nil {
		logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
		return err
	}
	return nil
}

func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) (io.ReadCloser, error) {
	req, err := http.NewRequest("POST", "/"+serviceMethod, data)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", versionMimetype)
	req.URL.Scheme = c.scheme
	req.URL.Host = c.addr

	var retries int
	start := time.Now()

	for {
		resp, err := c.http.Do(req)
		if err != nil {
			if !retry {
				return nil, err
			}

			timeOff := backoff(retries)
			if abort(start, timeOff) {
				return nil, err
			}
			retries++
			logrus.Warnf("Unable to connect to plugin: %s, retrying in %v", c.addr, timeOff)
			time.Sleep(timeOff)
			continue
		}

		if resp.StatusCode != http.StatusOK {
			b, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return nil, &statusError{resp.StatusCode, serviceMethod, err.Error()}
			}

			// Plugins' Response(s) should have an Err field indicating what went
			// wrong. Try to unmarshal into ResponseErr. Otherwise fallback to just
			// return the string(body)
			type responseErr struct {
				Err string
			}
			remoteErr := responseErr{}
			if err := json.Unmarshal(b, &remoteErr); err == nil {
				if remoteErr.Err != "" {
					return nil, &statusError{resp.StatusCode, serviceMethod, remoteErr.Err}
				}
			}
			// old way...
			return nil, &statusError{resp.StatusCode, serviceMethod, string(b)}
		}
		return resp.Body, nil
	}
}

func backoff(retries int) time.Duration {
	b, max := 1, defaultTimeOut
	for b < max && retries > 0 {
		b *= 2
		retries--
	}
	if b > max {
		b = max
	}
	return time.Duration(b) * time.Second
}

func abort(start time.Time, timeOff time.Duration) bool {
	return timeOff+time.Since(start) >= time.Duration(defaultTimeOut)*time.Second
}
<commit_msg>Close resp body on plugin call error<commit_after>package plugins

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go-connections/sockets"
	"github.com/docker/go-connections/tlsconfig"
)

const (
	versionMimetype = "application/vnd.docker.plugins.v1.2+json"
	defaultTimeOut  = 30
)

// NewClient creates a new plugin client (http).
func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
	tr := &http.Transport{}

	c, err := tlsconfig.Client(tlsConfig)
	if err != nil {
		return nil, err
	}
	tr.TLSClientConfig = c

	protoAndAddr := strings.Split(addr, "://")
	if err := sockets.ConfigureTransport(tr, protoAndAddr[0], protoAndAddr[1]); err != nil {
		return nil, err
	}

	scheme := protoAndAddr[0]
	if scheme != "https" {
		scheme = "http"
	}
	return &Client{&http.Client{Transport: tr}, scheme, protoAndAddr[1]}, nil
}

// Client represents a plugin client.
type Client struct {
	http   *http.Client // http client to use
	scheme string       // scheme protocol of the plugin
	addr   string       // http address of the plugin
}

// Call calls the specified method with the specified arguments for the plugin.
// It will retry for 30 seconds if a failure occurs when calling.
func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
	var buf bytes.Buffer
	if args != nil {
		if err := json.NewEncoder(&buf).Encode(args); err != nil {
			return err
		}
	}
	body, err := c.callWithRetry(serviceMethod, &buf, true)
	if err != nil {
		return err
	}
	defer body.Close()
	if ret != nil {
		if err := json.NewDecoder(body).Decode(&ret); err != nil {
			logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
			return err
		}
	}
	return nil
}

// Stream calls the specified method with the specified arguments for the plugin and returns the response body
func (c *Client) Stream(serviceMethod string, args interface{}) (io.ReadCloser, error) {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(args); err != nil {
		return nil, err
	}
	return c.callWithRetry(serviceMethod, &buf, true)
}

// SendFile calls the specified method, and passes through the IO stream
func (c *Client) SendFile(serviceMethod string, data io.Reader, ret interface{}) error {
	body, err := c.callWithRetry(serviceMethod, data, true)
	if err != nil {
		return err
	}
	if err := json.NewDecoder(body).Decode(&ret); err != nil {
		logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
		return err
	}
	return nil
}

func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) (io.ReadCloser, error) {
	req, err := http.NewRequest("POST", "/"+serviceMethod, data)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", versionMimetype)
	req.URL.Scheme = c.scheme
	req.URL.Host = c.addr

	var retries int
	start := time.Now()

	for {
		resp, err := c.http.Do(req)
		if err != nil {
			if !retry {
				return nil, err
			}

			timeOff := backoff(retries)
			if abort(start, timeOff) {
				return nil, err
			}
			retries++
			logrus.Warnf("Unable to connect to plugin: %s, retrying in %v", c.addr, timeOff)
			time.Sleep(timeOff)
			continue
		}

		if resp.StatusCode != http.StatusOK {
			b, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				return nil, &statusError{resp.StatusCode, serviceMethod, err.Error()}
			}

			// Plugins' Response(s) should have an Err field indicating what went
			// wrong. Try to unmarshal into ResponseErr. Otherwise fallback to just
			// return the string(body)
			type responseErr struct {
				Err string
			}
			remoteErr := responseErr{}
			if err := json.Unmarshal(b, &remoteErr); err == nil {
				if remoteErr.Err != "" {
					return nil, &statusError{resp.StatusCode, serviceMethod, remoteErr.Err}
				}
			}
			// old way...
			return nil, &statusError{resp.StatusCode, serviceMethod, string(b)}
		}
		return resp.Body, nil
	}
}

func backoff(retries int) time.Duration {
	b, max := 1, defaultTimeOut
	for b < max && retries > 0 {
		b *= 2
		retries--
	}
	if b > max {
		b = max
	}
	return time.Duration(b) * time.Second
}

func abort(start time.Time, timeOff time.Duration) bool {
	return timeOff+time.Since(start) >= time.Duration(defaultTimeOut)*time.Second
}
<|endoftext|>


<commit_before>package hostport

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNew(t *testing.T) {
	for i, tt := range []struct {
		hostPort string
		host     string
		hostFQDN string
		port     int
		err      string
	}{
		{"127.0.0.1:1234", "127.0.0.1", "127.0.0.1", 1234, ""},
		{"example.net", "", "", NoPort, "missing port in address"},
		{"example.net:1337", "example.net", "example.net.", 1337, ""},
		{"example.net.:1337", "example.net.", "example.net.", 1337, ""},
		{"[example.net.]:1337", "example.net.", "example.net.", 1337, ""},
		{"[127.0.0.1]:1337", "127.0.0.1", "127.0.0.1", 1337, ""},
		{"[example.net]:1337", "example.net", "example.net.", 1337, ""},
		{"[[example.net]]:1337", "", "", NoPort, "missing port in address"},
		{"", "", "", NoPort, "missing port in address"},
		{":", "", "", NoPort, "invalid syntax"},
		{"::", "", "", NoPort, "too many colons in address"},
		{"2001:DB8::1337", "", "", NoPort, "too many colons in address"},
		{"[2001:DB8::1337]:1337", "2001:db8::1337", "2001:db8::1337", 1337, ""},
		{"[2001:DB8::1337]:91337", "2001:db8::1337", "2001:db8::1337", NoPort, "must be between 0 and 65535"},
		{"[2001:DB8::1337]:007", "2001:db8::1337", "2001:db8::1337", 7, ""},
		{"[2001:DB8::1337]:-12", "2001:db8::1337", "2001:db8::1337", NoPort, "must be between 0 and 65535"},
		{"[2001:DB8::1337]:https", "2001:db8::1337", "2001:db8::1337", NoPort, "invalid syntax"},
		{"🔐.example.com:123", "xn--jv8h.example.com", "xn--jv8h.example.com.", 123, ""},
		{"🔐.example.com:007", "xn--jv8h.example.com", "xn--jv8h.example.com.", 7, ""},
	} {
		t.Run(fmt.Sprintf("%d:%s", i+1, tt.hostPort), func(t *testing.T) {
			a := assert.New(t)
			hp, err := New(tt.hostPort, false)
			a.Equal(tt.host, hp.Host)
			a.Equal(tt.port, hp.Port)
			if tt.err != "" {
				a.ErrorContains(err, tt.err)
			} else {
				a.NoError(err)
			}
			// FQDN tests
			hp, err = New(tt.hostPort, true)
			a.Equal(tt.hostFQDN, hp.Host)
			a.Equal(tt.port, hp.Port)
			if tt.err == "" {
				a.NoError(err)
			} else {
				a.ErrorContains(err, tt.err)
			}
		})
	}
}

func TestNewWithScheme(t *testing.T) {
	var tests = []struct {
		scheme    string
		hostPort  string
		host      string
		port      int
		forceFQDN bool
		errorMsg  string
	}{
		{"http", "example.com", "example.com", 80, false, ""},
		{"http", "127.0.0.1", "127.0.0.1", 80, false, ""},
		{"https", "127.0.0.1:123", "127.0.0.1", 123, false, ""},
		{"https", "[2001:DB8::1337]", "", 443, false, "invalid domain '[2001:DB8::1337]': idna: disallowed rune U+005B"},
		{"https", "2001:DB8::1337", "2001:db8::1337", 443, false, ""},
		{"https", "[2001:DB8::1337]:443", "2001:db8::1337", 443, false, ""},
		{"https", "[2001:db8::1337]:443", "2001:db8::1337", 443, false, ""},
		{"https", "[2001:DB8::1337]:-1", "2001:DB8::1337", NoPort, false, "invalid port number -1: must be between 0 and 65535"},
		{"https", "[2001:db8::1337]:111111", "2001:db8::1337", NoPort, false, "invalid port number 111111: must be between 0 and 65535"},
		{"unknown", "[[2001:DB8::1337]]", "[[2001:DB8::1337]]", NoPort, false, "unable to determine port for unknown"},
		{"https", "🔐.example.com:123", "xn--jv8h.example.com", 123, false, ""},
		{"smtp", "✉.example.com.", "xn--4bi.example.com.", 25, false, ""},
		{"https", "🔐.example.com:123", "xn--jv8h.example.com.", 123, true, ""},
		{"https", "🔐.example.com:007", "xn--jv8h.example.com.", 7, true, ""},
		{"https", "FOO-BAR.example.com", "foo-bar.example.com.", 443, true, ""},
		{"smtp", "✉.example.com", "xn--4bi.example.com.", 25, true, ""},
	}

	for _, tt := range tests {
		testname := fmt.Sprintf("%v://%v", tt.scheme, tt.hostPort)
		t.Run(testname, func(t *testing.T) {
			r := require.New(t)

			hp, err := NewWithScheme(tt.hostPort, tt.scheme, tt.forceFQDN)
			r.Equal(tt.host, hp.Host)
			r.Equal(tt.port, hp.Port)

			if err != nil {
				r.EqualError(err, tt.errorMsg)
			} else {
				r.NoError(err)
			}
		})
	}
}
<commit_msg>(test) Fix error checks<commit_after>package hostport

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNew(t *testing.T) {
	for i, tt := range []struct {
		hostPort string
		host     string
		hostFQDN string
		port     int
		err      string
	}{
		{"127.0.0.1:1234", "127.0.0.1", "127.0.0.1", 1234, ""},
		{"example.net", "", "", NoPort, "missing port in address"},
		{"example.net:1337", "example.net", "example.net.", 1337, ""},
		{"example.net.:1337", "example.net.", "example.net.", 1337, ""},
		{"[example.net.]:1337", "example.net.", "example.net.", 1337, ""},
		{"[127.0.0.1]:1337", "127.0.0.1", "127.0.0.1", 1337, ""},
		{"[example.net]:1337", "example.net", "example.net.", 1337, ""},
		{"[[example.net]]:1337", "", "", NoPort, "missing port in address"},
		{"", "", "", NoPort, "missing port in address"},
		{":", "", "", NoPort, "invalid syntax"},
		{"::", "", "", NoPort, "too many colons in address"},
		{"2001:DB8::1337", "", "", NoPort, "too many colons in address"},
		{"[2001:DB8::1337]:1337", "2001:db8::1337", "2001:db8::1337", 1337, ""},
		{"[2001:DB8::1337]:91337", "2001:db8::1337", "2001:db8::1337", NoPort, "must be between 0 and 65535"},
		{"[2001:DB8::1337]:007", "2001:db8::1337", "2001:db8::1337", 7, ""},
		{"[2001:DB8::1337]:-12", "2001:db8::1337", "2001:db8::1337", NoPort, "must be between 0 and 65535"},
		{"[2001:DB8::1337]:https", "2001:db8::1337", "2001:db8::1337", NoPort, "invalid syntax"},
		{"🔐.example.com:123", "xn--jv8h.example.com", "xn--jv8h.example.com.", 123, ""},
		{"🔐.example.com:007", "xn--jv8h.example.com", "xn--jv8h.example.com.", 7, ""},
	} {
		t.Run(fmt.Sprintf("%d:%s", i+1, tt.hostPort), func(t *testing.T) {
			a := assert.New(t)
			hp, err := New(tt.hostPort, false)
			a.Equal(tt.host, hp.Host)
			a.Equal(tt.port, hp.Port)
			if tt.err != "" {
				a.ErrorContains(err, tt.err)
			} else {
				a.NoError(err)
			}
			// FQDN tests
			hp, err = New(tt.hostPort, true)
			a.Equal(tt.hostFQDN, hp.Host)
			a.Equal(tt.port, hp.Port)
			if tt.err == "" {
				a.NoError(err)
			} else {
				a.ErrorContains(err, tt.err)
			}
		})
	}
}

func TestNewWithScheme(t *testing.T) {
	var tests = []struct {
		scheme    string
		hostPort  string
		host      string
		port      int
		forceFQDN bool
		err       string
	}{
		{"http", "example.com", "example.com", 80, false, ""},
		{"http", "127.0.0.1", "127.0.0.1", 80, false, ""},
		{"https", "127.0.0.1:123", "127.0.0.1", 123, false, ""},
		{"https", "[2001:DB8::1337]", "", 443, false, "invalid domain '[2001:DB8::1337]': idna: disallowed rune U+005B"},
		{"https", "2001:DB8::1337", "2001:db8::1337", 443, false, ""},
		{"https", "[2001:DB8::1337]:443", "2001:db8::1337", 443, false, ""},
		{"https", "[2001:db8::1337]:443", "2001:db8::1337", 443, false, ""},
		{"https", "[2001:DB8::1337]:-1", "2001:DB8::1337", NoPort, false, "invalid port number -1: must be between 0 and 65535"},
		{"https", "[2001:db8::1337]:111111", "2001:db8::1337", NoPort, false, "invalid port number 111111: must be between 0 and 65535"},
		{"unknown", "[[2001:DB8::1337]]", "[[2001:DB8::1337]]", NoPort, false, "unable to determine port for unknown"},
		{"https", "🔐.example.com:123", "xn--jv8h.example.com", 123, false, ""},
		{"smtp", "✉.example.com.", "xn--4bi.example.com.", 25, false, ""},
		{"https", "🔐.example.com:123", "xn--jv8h.example.com.", 123, true, ""},
		{"https", "🔐.example.com:007", "xn--jv8h.example.com.", 7, true, ""},
		{"https", "FOO-BAR.example.com", "foo-bar.example.com.", 443, true, ""},
		{"smtp", "✉.example.com", "xn--4bi.example.com.", 25, true, ""},
	}

	for _, tt := range tests {
		testname := fmt.Sprintf("%v://%v", tt.scheme, tt.hostPort)
		t.Run(testname, func(t *testing.T) {
			r := require.New(t)

			hp, err := NewWithScheme(tt.hostPort, tt.scheme, tt.forceFQDN)
			r.Equal(tt.host, hp.Host)
			r.Equal(tt.port, hp.Port)

			if tt.err != "" {
				r.EqualError(err, tt.err)
			} else {
				r.NoError(err)
			}
		})
	}
}
<|endoftext|>


<commit_before>package actors

import (
	"errors"
	"fmt"

	"github.com/cloudfoundry/cli/cf/actors/plan_builder"
	"github.com/cloudfoundry/cli/cf/actors/service_builder"
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/models"
)

type ServicePlanActor interface {
	FindServiceAccess(string) (ServiceAccess, error)
	UpdateAllPlansForService(string, bool) (bool, error)
	UpdateOrgForService(string, string, bool) (bool, error)
	UpdateSinglePlanForService(string, string, bool) (PlanAccess, error)
	UpdatePlanAndOrgForService(string, string, string, bool) (PlanAccess, error)
}

type PlanAccess int

const (
	PlanAccessError PlanAccess = iota
	All
	Limited
	None
)

type ServiceAccess int

const (
	ServiceAccessError ServiceAccess = iota
	AllPlansArePublic
	AllPlansArePrivate
	AllPlansAreLimited
	SomePlansArePublicSomeAreLimited
	SomePlansArePublicSomeArePrivate
	SomePlansAreLimitedSomeArePrivate
	SomePlansArePublicSomeAreLimitedSomeArePrivate
)

type ServicePlanHandler struct {
	servicePlanRepo           api.ServicePlanRepository
	servicePlanVisibilityRepo api.ServicePlanVisibilityRepository
	orgRepo                   api.OrganizationRepository
	serviceBuilder            service_builder.ServiceBuilder
	planBuilder               plan_builder.PlanBuilder
}

func NewServicePlanHandler(plan api.ServicePlanRepository, vis api.ServicePlanVisibilityRepository, org api.OrganizationRepository, planBuilder plan_builder.PlanBuilder, serviceBuilder service_builder.ServiceBuilder) ServicePlanHandler {
	return ServicePlanHandler{
		servicePlanRepo:           plan,
		servicePlanVisibilityRepo: vis,
		orgRepo:                   org,
		serviceBuilder:            serviceBuilder,
		planBuilder:               planBuilder,
	}
}

func (actor ServicePlanHandler) UpdateAllPlansForService(serviceName string, setPlanVisibility bool) (bool, error) {
	service, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return false, err
	}

	allPlansWereSet := true
	for _, plan := range service.Plans {
		planAccess, err := actor.updateSinglePlan(service, plan.Name, setPlanVisibility)
		if err != nil {
			return false, err
		}
		// If any plan is Limited we know that we have to change the visibility.
		planAlreadySet := ((planAccess == All) == setPlanVisibility) && planAccess != Limited
		allPlansWereSet = allPlansWereSet && planAlreadySet
	}
	return allPlansWereSet, nil
}

func (actor ServicePlanHandler) UpdateOrgForService(serviceName string, orgName string, setPlanVisibility bool) (bool, error) {
	var err error
	var service models.ServiceOffering

	service, err = actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return false, err
	}

	org, err := actor.orgRepo.FindByName(orgName)
	if err != nil {
		return false, err
	}

	allPlansWereSet := true
	for _, plan := range service.Plans {
		visibilityExists := plan.OrgHasVisibility(org.Name)
		if plan.Public || visibilityExists == setPlanVisibility {
			continue
		} else if visibilityExists && !setPlanVisibility {
			actor.deleteServicePlanVisibilities(map[string]string{"org_guid": org.Guid, "service_plan_guid": plan.Guid})
		} else if !visibilityExists && setPlanVisibility {
			err = actor.servicePlanVisibilityRepo.Create(plan.Guid, org.Guid)
			if err != nil {
				return false, err
			}
		}
		// We only get here once we have already updated a plan.
		allPlansWereSet = false
	}
	return allPlansWereSet, nil
}

func (actor ServicePlanHandler) UpdatePlanAndOrgForService(serviceName, planName, orgName string, setPlanVisibility bool) (PlanAccess, error) {
	service, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return PlanAccessError, err
	}

	org, err := actor.orgRepo.FindByName(orgName)
	if err != nil {
		return PlanAccessError, err
	}

	found := false
	var servicePlan models.ServicePlanFields
	for i, val := range service.Plans {
		if val.Name == planName {
			found = true
			servicePlan = service.Plans[i]
		}
	}
	if !found {
		return PlanAccessError, errors.New(fmt.Sprintf("Service plan %s not found", planName))
	}

	if !servicePlan.Public && setPlanVisibility {
		if servicePlan.OrgHasVisibility(orgName) {
			return Limited, nil
		}

		// Enable service access
		err = actor.servicePlanVisibilityRepo.Create(servicePlan.Guid, org.Guid)
		if err != nil {
			return PlanAccessError, err
		}
	} else if !servicePlan.Public && !setPlanVisibility {
		// Disable service access
		if servicePlan.OrgHasVisibility(org.Name) {
			err = actor.deleteServicePlanVisibilities(map[string]string{"organization_guid": org.Guid, "service_plan_guid": servicePlan.Guid})
			if err != nil {
				return PlanAccessError, err
			}
		}
	}

	access := actor.findPlanAccess(servicePlan)
	return access, nil
}

func (actor ServicePlanHandler) UpdateSinglePlanForService(serviceName string, planName string, setPlanVisibility bool) (PlanAccess, error) {
	serviceOffering, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return PlanAccessError, err
	}
	return actor.updateSinglePlan(serviceOffering, planName, setPlanVisibility)
}

func (actor ServicePlanHandler) updateSinglePlan(serviceOffering models.ServiceOffering, planName string, setPlanVisibility bool) (PlanAccess, error) {
	var planToUpdate *models.ServicePlanFields

	//find the service plan and set it as the only service plan for update
	for _, servicePlan := range serviceOffering.Plans {
		if servicePlan.Name == planName {
			planToUpdate = &servicePlan //he has the orgs inside him!!!
			break
		}
	}

	if planToUpdate == nil {
		return PlanAccessError, errors.New(fmt.Sprintf("The plan %s could not be found for service %s", planName, serviceOffering.Label))
	}

	err := actor.updateServicePlanAvailability(serviceOffering.Guid, *planToUpdate, setPlanVisibility)
	if err != nil {
		return PlanAccessError, err
	}

	access := actor.findPlanAccess(*planToUpdate)
	return access, nil
}

func (actor ServicePlanHandler) deleteServicePlanVisibilities(queryParams map[string]string) error {
	visibilities, err := actor.servicePlanVisibilityRepo.Search(queryParams)
	if err != nil {
		return err
	}
	for _, visibility := range visibilities {
		err = actor.servicePlanVisibilityRepo.Delete(visibility.Guid)
		if err != nil {
			return err
		}
	}

	return nil
}

func (actor ServicePlanHandler) updateServicePlanAvailability(serviceGuid string, servicePlan models.ServicePlanFields, setPlanVisibility bool) error {
	// We delete all service plan visibilities for the given Plan since the attribute public should function as a giant on/off
	// switch for all orgs. Thus we need to clean up any visibilities laying around so that they don't carry over.
	err := actor.deleteServicePlanVisibilities(map[string]string{"plan_guid": servicePlan.Guid})
	if err != nil {
		return err
	}

	if servicePlan.Public == setPlanVisibility {
		return nil
	}

	return actor.servicePlanRepo.Update(servicePlan, serviceGuid, setPlanVisibility)
}

func (actor ServicePlanHandler) FindServiceAccess(serviceName string) (ServiceAccess, error) {
	service, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return ServiceAccessError, err
	}

	publicBucket, limitedBucket, privateBucket := 0, 0, 0

	for _, plan := range service.Plans {
		if plan.Public {
			publicBucket++
		} else if len(plan.OrgNames) > 0 {
			limitedBucket++
		} else {
			privateBucket++
		}
	}

	if publicBucket > 0 && limitedBucket == 0 && privateBucket == 0 {
		return AllPlansArePublic, nil
	}
	if publicBucket > 0 && limitedBucket > 0 && privateBucket == 0 {
		return SomePlansArePublicSomeAreLimited, nil
	}
	if publicBucket > 0 && privateBucket > 0 && limitedBucket == 0 {
		return SomePlansArePublicSomeArePrivate, nil
	}

	if limitedBucket > 0 && publicBucket == 0 && privateBucket == 0 {
		return AllPlansAreLimited, nil
	}
	if privateBucket > 0 && publicBucket == 0 && privateBucket == 0 {
		return AllPlansArePrivate, nil
	}
	if limitedBucket > 0 && privateBucket > 0 && publicBucket == 0 {
		return SomePlansAreLimitedSomeArePrivate, nil
	}
	return SomePlansArePublicSomeAreLimitedSomeArePrivate, nil
}

func (actor ServicePlanHandler) findPlanAccess(plan models.ServicePlanFields) PlanAccess {
	if plan.Public {
		return All
	} else if len(plan.OrgNames) > 0 {
		return Limited
	} else {
		return None
	}
}
<commit_msg>Use correct parameter for querying service plan visibilities.<commit_after>package actors

import (
	"errors"
	"fmt"

	"github.com/cloudfoundry/cli/cf/actors/plan_builder"
	"github.com/cloudfoundry/cli/cf/actors/service_builder"
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/models"
)

type ServicePlanActor interface {
	FindServiceAccess(string) (ServiceAccess, error)
	UpdateAllPlansForService(string, bool) (bool, error)
	UpdateOrgForService(string, string, bool) (bool, error)
	UpdateSinglePlanForService(string, string, bool) (PlanAccess, error)
	UpdatePlanAndOrgForService(string, string, string, bool) (PlanAccess, error)
}

type PlanAccess int

const (
	PlanAccessError PlanAccess = iota
	All
	Limited
	None
)

type ServiceAccess int

const (
	ServiceAccessError ServiceAccess = iota
	AllPlansArePublic
	AllPlansArePrivate
	AllPlansAreLimited
	SomePlansArePublicSomeAreLimited
	SomePlansArePublicSomeArePrivate
	SomePlansAreLimitedSomeArePrivate
	SomePlansArePublicSomeAreLimitedSomeArePrivate
)

type ServicePlanHandler struct {
	servicePlanRepo           api.ServicePlanRepository
	servicePlanVisibilityRepo api.ServicePlanVisibilityRepository
	orgRepo                   api.OrganizationRepository
	serviceBuilder            service_builder.ServiceBuilder
	planBuilder               plan_builder.PlanBuilder
}

func NewServicePlanHandler(plan api.ServicePlanRepository, vis api.ServicePlanVisibilityRepository, org api.OrganizationRepository, planBuilder plan_builder.PlanBuilder, serviceBuilder service_builder.ServiceBuilder) ServicePlanHandler {
	return ServicePlanHandler{
		servicePlanRepo:           plan,
		servicePlanVisibilityRepo: vis,
		orgRepo:                   org,
		serviceBuilder:            serviceBuilder,
		planBuilder:               planBuilder,
	}
}

func (actor ServicePlanHandler) UpdateAllPlansForService(serviceName string, setPlanVisibility bool) (bool, error) {
	service, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return false, err
	}

	allPlansWereSet := true
	for _, plan := range service.Plans {
		planAccess, err := actor.updateSinglePlan(service, plan.Name, setPlanVisibility)
		if err != nil {
			return false, err
		}
		// If any plan is Limited we know that we have to change the visibility.
		planAlreadySet := ((planAccess == All) == setPlanVisibility) && planAccess != Limited
		allPlansWereSet = allPlansWereSet && planAlreadySet
	}
	return allPlansWereSet, nil
}

func (actor ServicePlanHandler) UpdateOrgForService(serviceName string, orgName string, setPlanVisibility bool) (bool, error) {
	var err error
	var service models.ServiceOffering

	service, err = actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return false, err
	}

	org, err := actor.orgRepo.FindByName(orgName)
	if err != nil {
		return false, err
	}

	allPlansWereSet := true
	for _, plan := range service.Plans {
		visibilityExists := plan.OrgHasVisibility(org.Name)
		if plan.Public || visibilityExists == setPlanVisibility {
			continue
		} else if visibilityExists && !setPlanVisibility {
			actor.deleteServicePlanVisibilities(map[string]string{"org_guid": org.Guid, "service_plan_guid": plan.Guid})
		} else if !visibilityExists && setPlanVisibility {
			err = actor.servicePlanVisibilityRepo.Create(plan.Guid, org.Guid)
			if err != nil {
				return false, err
			}
		}
		// We only get here once we have already updated a plan.
		allPlansWereSet = false
	}
	return allPlansWereSet, nil
}

func (actor ServicePlanHandler) UpdatePlanAndOrgForService(serviceName, planName, orgName string, setPlanVisibility bool) (PlanAccess, error) {
	service, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return PlanAccessError, err
	}

	org, err := actor.orgRepo.FindByName(orgName)
	if err != nil {
		return PlanAccessError, err
	}

	found := false
	var servicePlan models.ServicePlanFields
	for i, val := range service.Plans {
		if val.Name == planName {
			found = true
			servicePlan = service.Plans[i]
		}
	}
	if !found {
		return PlanAccessError, errors.New(fmt.Sprintf("Service plan %s not found", planName))
	}

	if !servicePlan.Public && setPlanVisibility {
		if servicePlan.OrgHasVisibility(orgName) {
			return Limited, nil
		}

		// Enable service access
		err = actor.servicePlanVisibilityRepo.Create(servicePlan.Guid, org.Guid)
		if err != nil {
			return PlanAccessError, err
		}
	} else if !servicePlan.Public && !setPlanVisibility {
		// Disable service access
		if servicePlan.OrgHasVisibility(org.Name) {
			err = actor.deleteServicePlanVisibilities(map[string]string{"organization_guid": org.Guid, "service_plan_guid": servicePlan.Guid})
			if err != nil {
				return PlanAccessError, err
			}
		}
	}

	access := actor.findPlanAccess(servicePlan)
	return access, nil
}

func (actor ServicePlanHandler) UpdateSinglePlanForService(serviceName string, planName string, setPlanVisibility bool) (PlanAccess, error) {
	serviceOffering, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return PlanAccessError, err
	}
	return actor.updateSinglePlan(serviceOffering, planName, setPlanVisibility)
}

func (actor ServicePlanHandler) updateSinglePlan(serviceOffering models.ServiceOffering, planName string, setPlanVisibility bool) (PlanAccess, error) {
	var planToUpdate *models.ServicePlanFields

	//find the service plan and set it as the only service plan for update
	for _, servicePlan := range serviceOffering.Plans {
		if servicePlan.Name == planName {
			planToUpdate = &servicePlan //he has the orgs inside him!!!
			break
		}
	}

	if planToUpdate == nil {
		return PlanAccessError, errors.New(fmt.Sprintf("The plan %s could not be found for service %s", planName, serviceOffering.Label))
	}

	err := actor.updateServicePlanAvailability(serviceOffering.Guid, *planToUpdate, setPlanVisibility)
	if err != nil {
		return PlanAccessError, err
	}

	access := actor.findPlanAccess(*planToUpdate)
	return access, nil
}

func (actor ServicePlanHandler) deleteServicePlanVisibilities(queryParams map[string]string) error {
	visibilities, err := actor.servicePlanVisibilityRepo.Search(queryParams)
	if err != nil {
		return err
	}
	for _, visibility := range visibilities {
		err = actor.servicePlanVisibilityRepo.Delete(visibility.Guid)
		if err != nil {
			return err
		}
	}

	return nil
}

func (actor ServicePlanHandler) updateServicePlanAvailability(serviceGuid string, servicePlan models.ServicePlanFields, setPlanVisibility bool) error {
	// We delete all service plan visibilities for the given Plan since the attribute public should function as a giant on/off
	// switch for all orgs. Thus we need to clean up any visibilities laying around so that they don't carry over.
	err := actor.deleteServicePlanVisibilities(map[string]string{"service_plan_guid": servicePlan.Guid})
	if err != nil {
		return err
	}

	if servicePlan.Public == setPlanVisibility {
		return nil
	}

	return actor.servicePlanRepo.Update(servicePlan, serviceGuid, setPlanVisibility)
}

func (actor ServicePlanHandler) FindServiceAccess(serviceName string) (ServiceAccess, error) {
	service, err := actor.serviceBuilder.GetServiceByName(serviceName)
	if err != nil {
		return ServiceAccessError, err
	}

	publicBucket, limitedBucket, privateBucket := 0, 0, 0

	for _, plan := range service.Plans {
		if plan.Public {
			publicBucket++
		} else if len(plan.OrgNames) > 0 {
			limitedBucket++
		} else {
			privateBucket++
		}
	}

	if publicBucket > 0 && limitedBucket == 0 && privateBucket == 0 {
		return AllPlansArePublic, nil
	}
	if publicBucket > 0 && limitedBucket > 0 && privateBucket == 0 {
		return SomePlansArePublicSomeAreLimited, nil
	}
	if publicBucket > 0 && privateBucket > 0 && limitedBucket == 0 {
		return SomePlansArePublicSomeArePrivate, nil
	}

	if limitedBucket > 0 && publicBucket == 0 && privateBucket == 0 {
		return AllPlansAreLimited, nil
	}
	if privateBucket > 0 && publicBucket == 0 && privateBucket == 0 {
		return AllPlansArePrivate, nil
	}
	if limitedBucket > 0 && privateBucket > 0 && publicBucket == 0 {
		return SomePlansAreLimitedSomeArePrivate, nil
	}
	return SomePlansArePublicSomeAreLimitedSomeArePrivate, nil
}

func (actor ServicePlanHandler) findPlanAccess(plan models.ServicePlanFields) PlanAccess {
	if plan.Public {
		return All
	} else if len(plan.OrgNames) > 0 {
		return Limited
	} else {
		return None
	}
}
<|endoftext|>


<commit_before>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 Julien Schmidt. All rights reserved.
// http://www.julienschmidt.com
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"database/sql/driver"
	"errors"
	"net"
	"strings"
)

type mysqlConn struct {
	cfg              *config
	flags            clientFlag
	charset          byte
	cipher           []byte
	netConn          net.Conn
	buf              *buffer
	protocol         uint8
	sequence         uint8
	affectedRows     uint64
	insertId         uint64
	maxPacketAllowed int
	maxWriteSize     int
}

type config struct {
	user   string
	passwd string
	net    string
	addr   string
	dbname string
	params map[string]string
}

// Handles parameters set in DSN
func (mc *mysqlConn) handleParams() (err error) {
	for param, val := range mc.cfg.params {
		switch param {
		// Charset
		case "charset":
			charsets := strings.Split(val, ",")
			for i := range charsets {
				// ignore errors here - a charset may not exist
				err = mc.exec("SET NAMES " + charsets[i])
				if err == nil {
					break
				}
			}
			if err != nil {
				return
			}

		// handled elsewhere
		case "timeout", "allowAllFiles":
			continue

		// TLS-Encryption
		case "tls":
			err = errors.New("TLS-Encryption not implemented yet")
			return

		// Compression
		case "compress":
			err = errors.New("Compression not implemented yet")

		// System Vars
		default:
			err = mc.exec("SET " + param + "=" + val + "")
			if err != nil {
				return
			}
		}
	}

	return
}

func (mc *mysqlConn) Begin() (driver.Tx, error) {
	err := mc.exec("START TRANSACTION")
	if err == nil {
		return &mysqlTx{mc}, err
	}

	return nil, err
}

func (mc *mysqlConn) Close() (err error) {
	mc.writeCommandPacket(comQuit)
	mc.cfg = nil
	mc.buf = nil
	mc.netConn.Close()
	mc.netConn = nil
	return
}

func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
	// Send command
	err := mc.writeCommandPacketStr(comStmtPrepare, query)
	if err != nil {
		return nil, err
	}

	stmt := &mysqlStmt{
		mc: mc,
	}

	// Read Result
	columnCount, err := stmt.readPrepareResultPacket()
	if err == nil {
		if stmt.paramCount > 0 {
			stmt.params, err = stmt.mc.readColumns(stmt.paramCount)
			if err != nil {
				return nil, err
			}
		}

		if columnCount > 0 {
			err = stmt.mc.readUntilEOF()
		}
	}

	return stmt, err
}

func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
	if len(args) == 0 { // no args, fastpath
		mc.affectedRows = 0
		mc.insertId = 0

		err := mc.exec(query)
		if err == nil {
			return &mysqlResult{
				affectedRows: int64(mc.affectedRows),
				insertId:     int64(mc.insertId),
			}, err
		}
		return nil, err
	}

	// with args, must use prepared stmt
	return nil, driver.ErrSkip

}

// Internal function to execute commands
func (mc *mysqlConn) exec(query string) (err error) {
	// Send command
	err = mc.writeCommandPacketStr(comQuery, query)
	if err != nil {
		return
	}

	// Read Result
	var resLen int
	resLen, err = mc.readResultSetHeaderPacket()
	if err == nil && resLen > 0 {
		err = mc.readUntilEOF()
		if err != nil {
			return
		}

		err = mc.readUntilEOF()
	}

	return
}

func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
	if len(args) == 0 { // no args, fastpath
		// Send command
		err := mc.writeCommandPacketStr(comQuery, query)
		if err == nil {
			// Read Result
			var resLen int
			resLen, err = mc.readResultSetHeaderPacket()
			if err == nil {
				rows := &mysqlRows{mc, false, nil, false}

				if resLen > 0 {
					// Columns
					rows.columns, err = mc.readColumns(resLen)
				}
				return rows, err
			}
		}

		return nil, err
	}

	// with args, must use prepared stmt
	return nil, driver.ErrSkip
}

// Gets the value of the given MySQL System Variable
func (mc *mysqlConn) getSystemVar(name string) (val []byte, err error) {
	// Send command
	err = mc.writeCommandPacketStr(comQuery, "SELECT @@"+name)
	if err == nil {
		// Read Result
		var resLen int
		resLen, err = mc.readResultSetHeaderPacket()
		if err == nil {
			rows := &mysqlRows{mc, false, nil, false}

			if resLen > 0 {
				// Columns
				rows.columns, err = mc.readColumns(resLen)
			}

			dest := make([]driver.Value, resLen)
			err = rows.readRow(dest)
			if err == nil {
				val = dest[0].([]byte)
				err = mc.readUntilEOF()
			}
		}
	}

	return
}
<commit_msg>getSystemVar result validity doc<commit_after>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 Julien Schmidt. All rights reserved.
// http://www.julienschmidt.com
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"database/sql/driver"
	"errors"
	"net"
	"strings"
)

type mysqlConn struct {
	cfg              *config
	flags            clientFlag
	charset          byte
	cipher           []byte
	netConn          net.Conn
	buf              *buffer
	protocol         uint8
	sequence         uint8
	affectedRows     uint64
	insertId         uint64
	maxPacketAllowed int
	maxWriteSize     int
}

type config struct {
	user   string
	passwd string
	net    string
	addr   string
	dbname string
	params map[string]string
}

// Handles parameters set in DSN
func (mc *mysqlConn) handleParams() (err error) {
	for param, val := range mc.cfg.params {
		switch param {
		// Charset
		case "charset":
			charsets := strings.Split(val, ",")
			for i := range charsets {
				// ignore errors here - a charset may not exist
				err = mc.exec("SET NAMES " + charsets[i])
				if err == nil {
					break
				}
			}
			if err != nil {
				return
			}

		// handled elsewhere
		case "timeout", "allowAllFiles":
			continue

		// TLS-Encryption
		case "tls":
			err = errors.New("TLS-Encryption not implemented yet")
			return

		// Compression
		case "compress":
			err = errors.New("Compression not implemented yet")

		// System Vars
		default:
			err = mc.exec("SET " + param + "=" + val + "")
			if err != nil {
				return
			}
		}
	}

	return
}

func (mc *mysqlConn) Begin() (driver.Tx, error) {
	err := mc.exec("START TRANSACTION")
	if err == nil {
		return &mysqlTx{mc}, err
	}

	return nil, err
}

func (mc *mysqlConn) Close() (err error) {
	mc.writeCommandPacket(comQuit)
	mc.cfg = nil
	mc.buf = nil
	mc.netConn.Close()
	mc.netConn = nil
	return
}

func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
	// Send command
	err := mc.writeCommandPacketStr(comStmtPrepare, query)
	if err != nil {
		return nil, err
	}

	stmt := &mysqlStmt{
		mc: mc,
	}

	// Read Result
	columnCount, err := stmt.readPrepareResultPacket()
	if err == nil {
		if stmt.paramCount > 0 {
			stmt.params, err = stmt.mc.readColumns(stmt.paramCount)
			if err != nil {
				return nil, err
			}
		}

		if columnCount > 0 {
			err = stmt.mc.readUntilEOF()
		}
	}

	return stmt, err
}

func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
	if len(args) == 0 { // no args, fastpath
		mc.affectedRows = 0
		mc.insertId = 0

		err := mc.exec(query)
		if err == nil {
			return &mysqlResult{
				affectedRows: int64(mc.affectedRows),
				insertId:     int64(mc.insertId),
			}, err
		}
		return nil, err
	}

	// with args, must use prepared stmt
	return nil, driver.ErrSkip

}

// Internal function to execute commands
func (mc *mysqlConn) exec(query string) (err error) {
	// Send command
	err = mc.writeCommandPacketStr(comQuery, query)
	if err != nil {
		return
	}

	// Read Result
	var resLen int
	resLen, err = mc.readResultSetHeaderPacket()
	if err == nil && resLen > 0 {
		err = mc.readUntilEOF()
		if err != nil {
			return
		}

		err = mc.readUntilEOF()
	}

	return
}

func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
	if len(args) == 0 { // no args, fastpath
		// Send command
		err := mc.writeCommandPacketStr(comQuery, query)
		if err == nil {
			// Read Result
			var resLen int
			resLen, err = mc.readResultSetHeaderPacket()
			if err == nil {
				rows := &mysqlRows{mc, false, nil, false}

				if resLen > 0 {
					// Columns
					rows.columns, err = mc.readColumns(resLen)
				}
				return rows, err
			}
		}

		return nil, err
	}

	// with args, must use prepared stmt
	return nil, driver.ErrSkip
}

// Gets the value of the given MySQL System Variable
// The returned byte slice is only valid until the next read
func (mc *mysqlConn) getSystemVar(name string) (val []byte, err error) {
	// Send command
	err = mc.writeCommandPacketStr(comQuery, "SELECT @@"+name)
	if err == nil {
		// Read Result
		var resLen int
		resLen, err = mc.readResultSetHeaderPacket()
		if err == nil {
			rows := &mysqlRows{mc, false, nil, false}

			if resLen > 0 {
				// Columns
				rows.columns, err = mc.readColumns(resLen)
			}

			dest := make([]driver.Value, resLen)
			err = rows.readRow(dest)
			if err == nil {
				val = dest[0].([]byte)
				err = mc.readUntilEOF()
			}
		}
	}

	return
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 printers

import (
	"fmt"
	"strings"

	"github.com/spf13/cobra"

	"k8s.io/apimachinery/pkg/runtime"
)

var (
	internalObjectPrinterErr = "a versioned object must be passed to a printer"

	// disallowedPackagePrefixes contains regular expression templates
	// for object package paths that are not allowed by printers.
	disallowedPackagePrefixes = []string{
		"k8s.io/kubernetes/pkg/apis/",
	}
)

var internalObjectPreventer = &illegalPackageSourceChecker{disallowedPackagePrefixes}

type NoCompatiblePrinterError struct {
	OutputFormat *string
	Options      interface{}
}

func (e NoCompatiblePrinterError) Error() string {
	output := ""
	if e.OutputFormat != nil {
		output = *e.OutputFormat
	}

	return fmt.Sprintf("unable to match a printer suitable for the output format %q and the options specified: %#v", output, e.Options)
}

func IsNoCompatiblePrinterError(err error) bool {
	if err == nil {
		return false
	}

	_, ok := err.(NoCompatiblePrinterError)
	return ok
}

func IsInternalObjectError(err error) bool {
	if err == nil {
		return false
	}

	return err.Error() == internalObjectPrinterErr
}

// PrintFlags composes common printer flag structs
// used across all commands, and provides a method
// of retrieving a known printer based on flag values provided.
type PrintFlags struct {
	JSONYamlPrintFlags *JSONYamlPrintFlags
	NamePrintFlags     *NamePrintFlags

	OutputFormat *string

	Scheme runtime.ObjectConvertor
}

func (f *PrintFlags) Complete(successTemplate string) error {
	return f.NamePrintFlags.Complete(successTemplate)
}

func (f *PrintFlags) ToPrinter() (ResourcePrinter, error) {
	outputFormat := ""
	if f.OutputFormat != nil {
		outputFormat = *f.OutputFormat
	}

	if f.JSONYamlPrintFlags != nil {
		if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
			return p, err
		}
	}

	if f.NamePrintFlags != nil {
		if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
			return p, err
		}
	}

	return nil, NoCompatiblePrinterError{Options: f, OutputFormat: f.OutputFormat}
}

func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
	f.JSONYamlPrintFlags.AddFlags(cmd)
	f.NamePrintFlags.AddFlags(cmd)

	if f.OutputFormat != nil {
		cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
	}
}

// WithDefaultOutput sets a default output format if one is not provided through a flag value
func (f *PrintFlags) WithDefaultOutput(output string) *PrintFlags {
	f.OutputFormat = &output
	return f
}

func NewPrintFlags(operation string, scheme runtime.ObjectConvertor) *PrintFlags {
	outputFormat := ""

	return &PrintFlags{
		OutputFormat: &outputFormat,

		Scheme: scheme,

		JSONYamlPrintFlags: NewJSONYamlPrintFlags(scheme),
		NamePrintFlags:     NewNamePrintFlags(operation, scheme),
	}
}

// illegalPackageSourceChecker compares a given
// object's package path, and determines if the
// object originates from a disallowed source.
type illegalPackageSourceChecker struct {
	// disallowedPrefixes is a slice of disallowed package path
	// prefixes for a given runtime.Object that we are printing.
	disallowedPrefixes []string
}

func (c *illegalPackageSourceChecker) IsForbidden(pkgPath string) bool {
	for _, forbiddenPrefix := range c.disallowedPrefixes {
		if strings.HasPrefix(pkgPath, forbiddenPrefix) {
			return true
		}
	}

	return false
}
<commit_msg>remove unused PrintFlags.Scheme<commit_after>/*
Copyright 2018 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 printers

import (
	"fmt"
	"strings"

	"github.com/spf13/cobra"

	"k8s.io/apimachinery/pkg/runtime"
)

var (
	internalObjectPrinterErr = "a versioned object must be passed to a printer"

	// disallowedPackagePrefixes contains regular expression templates
	// for object package paths that are not allowed by printers.
	disallowedPackagePrefixes = []string{
		"k8s.io/kubernetes/pkg/apis/",
	}
)

var internalObjectPreventer = &illegalPackageSourceChecker{disallowedPackagePrefixes}

type NoCompatiblePrinterError struct {
	OutputFormat *string
	Options      interface{}
}

func (e NoCompatiblePrinterError) Error() string {
	output := ""
	if e.OutputFormat != nil {
		output = *e.OutputFormat
	}

	return fmt.Sprintf("unable to match a printer suitable for the output format %q and the options specified: %#v", output, e.Options)
}

func IsNoCompatiblePrinterError(err error) bool {
	if err == nil {
		return false
	}

	_, ok := err.(NoCompatiblePrinterError)
	return ok
}

func IsInternalObjectError(err error) bool {
	if err == nil {
		return false
	}

	return err.Error() == internalObjectPrinterErr
}

// PrintFlags composes common printer flag structs
// used across all commands, and provides a method
// of retrieving a known printer based on flag values provided.
type PrintFlags struct {
	JSONYamlPrintFlags *JSONYamlPrintFlags
	NamePrintFlags     *NamePrintFlags

	OutputFormat *string
}

func (f *PrintFlags) Complete(successTemplate string) error {
	return f.NamePrintFlags.Complete(successTemplate)
}

func (f *PrintFlags) ToPrinter() (ResourcePrinter, error) {
	outputFormat := ""
	if f.OutputFormat != nil {
		outputFormat = *f.OutputFormat
	}

	if f.JSONYamlPrintFlags != nil {
		if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
			return p, err
		}
	}

	if f.NamePrintFlags != nil {
		if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
			return p, err
		}
	}

	return nil, NoCompatiblePrinterError{Options: f, OutputFormat: f.OutputFormat}
}

func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
	f.JSONYamlPrintFlags.AddFlags(cmd)
	f.NamePrintFlags.AddFlags(cmd)

	if f.OutputFormat != nil {
		cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
	}
}

// WithDefaultOutput sets a default output format if one is not provided through a flag value
func (f *PrintFlags) WithDefaultOutput(output string) *PrintFlags {
	f.OutputFormat = &output
	return f
}

func NewPrintFlags(operation string, scheme runtime.ObjectConvertor) *PrintFlags {
	outputFormat := ""

	return &PrintFlags{
		OutputFormat: &outputFormat,

		JSONYamlPrintFlags: NewJSONYamlPrintFlags(scheme),
		NamePrintFlags:     NewNamePrintFlags(operation, scheme),
	}
}

// illegalPackageSourceChecker compares a given
// object's package path, and determines if the
// object originates from a disallowed source.
type illegalPackageSourceChecker struct {
	// disallowedPrefixes is a slice of disallowed package path
	// prefixes for a given runtime.Object that we are printing.
	disallowedPrefixes []string
}

func (c *illegalPackageSourceChecker) IsForbidden(pkgPath string) bool {
	for _, forbiddenPrefix := range c.disallowedPrefixes {
		if strings.HasPrefix(pkgPath, forbiddenPrefix) {
			return true
		}
	}

	return false
}
<|endoftext|>