// Returns a list of all Daemon Set model objects in the cluster, based on all Kubernetes
// Daemon Set and Service API objects.
// The function processes all Daemon Set API objects and finds matching Services for them.
func getDaemonSetList(daemonSets []extensions.DaemonSet, pods []api.Pod,
	events []api.Event) *DaemonSetList {

	daemonSetList := &DaemonSetList{DaemonSets: make([]DaemonSet, 0)}

	for _, daemonSet := range daemonSets {

		matchingPods := make([]api.Pod, 0)
		for _, pod := range pods {
			if pod.ObjectMeta.Namespace == daemonSet.ObjectMeta.Namespace &&
				common.IsLabelSelectorMatching(pod.ObjectMeta.Labels, daemonSet.Spec.Selector) {
				matchingPods = append(matchingPods, pod)
			}
		}
		podInfo := getDaemonSetPodInfo(&daemonSet, matchingPods)
		podErrors := event.GetPodsEventWarnings(events, matchingPods)

		podInfo.Warnings = podErrors

		daemonSetList.DaemonSets = append(daemonSetList.DaemonSets,
			DaemonSet{
				ObjectMeta:      common.NewObjectMeta(daemonSet.ObjectMeta),
				TypeMeta:        common.NewTypeMeta(common.ResourceKindDaemonSet),
				Pods:            podInfo,
				ContainerImages: common.GetContainerImages(&daemonSet.Spec.Template.Spec),
			})
	}

	return daemonSetList
}
func getDeploymentList(deployments []extensions.Deployment, pods []api.Pod,
	events []api.Event) *DeploymentList {

	deploymentList := &DeploymentList{
		Deployments: make([]Deployment, 0),
	}

	for _, deployment := range deployments {

		matchingPods := common.FilterNamespacedPodsBySelector(pods, deployment.ObjectMeta.Namespace,
			deployment.Spec.Selector.MatchLabels)
		podInfo := getPodInfo(&deployment, matchingPods)
		podInfo.Warnings = event.GetPodsEventWarnings(events, matchingPods)

		deploymentList.Deployments = append(deploymentList.Deployments,
			Deployment{
				ObjectMeta:      common.NewObjectMeta(deployment.ObjectMeta),
				TypeMeta:        common.NewTypeMeta(common.ResourceKindDeployment),
				ContainerImages: common.GetContainerImages(&deployment.Spec.Template.Spec),
				Pods:            podInfo,
			})
	}

	return deploymentList
}
// Returns a list of all Replication Controller model objects in the cluster, based on all Kubernetes
// Replication Controller and Service API objects.
func getReplicationControllerList(replicationControllers []api.ReplicationController,
	pods []api.Pod, events []api.Event) *ReplicationControllerList {

	replicationControllerList := &ReplicationControllerList{
		ReplicationControllers: make([]ReplicationController, 0),
	}

	for _, replicationController := range replicationControllers {
		matchingPods := make([]api.Pod, 0)
		for _, pod := range pods {
			if pod.ObjectMeta.Namespace == replicationController.ObjectMeta.Namespace &&
				common.IsSelectorMatching(replicationController.Spec.Selector, pod.ObjectMeta.Labels) {
				matchingPods = append(matchingPods, pod)
			}
		}
		podInfo := getReplicationPodInfo(&replicationController, matchingPods)
		podErrors := event.GetPodsEventWarnings(events, matchingPods)

		podInfo.Warnings = podErrors

		replicationControllerList.ReplicationControllers = append(replicationControllerList.ReplicationControllers,
			ReplicationController{
				ObjectMeta:      common.NewObjectMeta(replicationController.ObjectMeta),
				TypeMeta:        common.NewTypeMeta(common.ResourceKindReplicationController),
				Pods:            podInfo,
				ContainerImages: common.GetContainerImages(&replicationController.Spec.Template.Spec),
			})
	}

	return replicationControllerList
}
func ToPetSet(petSet *apps.PetSet, podInfo *common.PodInfo) PetSet {
	return PetSet{
		ObjectMeta:      common.NewObjectMeta(petSet.ObjectMeta),
		TypeMeta:        common.NewTypeMeta(common.ResourceKindPetSet),
		ContainerImages: common.GetContainerImages(&petSet.Spec.Template.Spec),
		Pods:            *podInfo,
	}
}
func ToReplicaSet(replicaSet *extensions.ReplicaSet, podInfo *common.PodInfo) ReplicaSet {
	return ReplicaSet{
		ObjectMeta:      common.NewObjectMeta(replicaSet.ObjectMeta),
		TypeMeta:        common.NewTypeMeta(common.ResourceKindReplicaSet),
		ContainerImages: common.GetContainerImages(&replicaSet.Spec.Template.Spec),
		Pods:            *podInfo,
	}
}
func ToJob(job *batch.Job, podInfo *common.PodInfo) Job {
	return Job{
		ObjectMeta:      common.NewObjectMeta(job.ObjectMeta),
		TypeMeta:        common.NewTypeMeta(common.ResourceKindJob),
		ContainerImages: common.GetContainerImages(&job.Spec.Template.Spec),
		Pods:            *podInfo,
	}
}
// ToServiceDetail returns api service object based on kubernetes service object
func ToServiceDetail(service *api.Service) ServiceDetail {
	return ServiceDetail{
		ObjectMeta:        common.NewObjectMeta(service.ObjectMeta),
		TypeMeta:          common.NewTypeMeta(common.ResourceKindService),
		InternalEndpoint:  common.GetInternalEndpoint(service.Name, service.Namespace, service.Spec.Ports),
		ExternalEndpoints: common.GetExternalEndpoints(service),
		Selector:          service.Spec.Selector,
		ClusterIP:         service.Spec.ClusterIP,
		Type:              service.Spec.Type,
	}
}
func ToPod(pod *api.Pod, metrics *MetricsByPod) Pod {
	podDetail := Pod{
		ObjectMeta:   common.NewObjectMeta(pod.ObjectMeta),
		TypeMeta:     common.NewTypeMeta(common.ResourceKindPod),
		PodPhase:     pod.Status.Phase,
		PodIP:        pod.Status.PodIP,
		RestartCount: getRestartCount(*pod),
	}

	if metrics != nil && metrics.MetricsMap[pod.Namespace] != nil {
		metric := metrics.MetricsMap[pod.Namespace][pod.Name]
		podDetail.Metrics = &metric
	}

	return podDetail
}
func getReplicaSetDetail(replicaSet *extensions.ReplicaSet, heapsterClient client.HeapsterClient,
	events *common.EventList, pods []api.Pod) ReplicaSetDetail {

	matchingPods := common.FilterNamespacedPodsByLabelSelector(pods, replicaSet.ObjectMeta.Namespace,
		replicaSet.Spec.Selector)

	podInfo := getPodInfo(replicaSet, matchingPods)

	return ReplicaSetDetail{
		ObjectMeta:      common.NewObjectMeta(replicaSet.ObjectMeta),
		TypeMeta:        common.NewTypeMeta(common.ResourceKindReplicaSet),
		ContainerImages: common.GetContainerImages(&replicaSet.Spec.Template.Spec),
		PodInfo:         podInfo,
		PodList:         pod.CreatePodList(matchingPods, heapsterClient),
		EventList:       *events,
	}
}
// AppendEvents appends events from source slice to target events representation.
func AppendEvents(source []api.Event, target common.EventList) common.EventList {
	for _, event := range source {
		target.Events = append(target.Events, common.Event{
			ObjectMeta:      common.NewObjectMeta(event.ObjectMeta),
			TypeMeta:        common.NewTypeMeta(common.ResourceKindEvent),
			Message:         event.Message,
			SourceComponent: event.Source.Component,
			SourceHost:      event.Source.Host,
			SubObject:       event.InvolvedObject.FieldPath,
			Count:           event.Count,
			FirstSeen:       event.FirstTimestamp,
			LastSeen:        event.LastTimestamp,
			Reason:          event.Reason,
			Type:            event.Type,
		})
	}
	return target
}
func getPetSetDetail(petSet *apps.PetSet, heapsterClient client.HeapsterClient,
	events *common.EventList, pods []api.Pod) PetSetDetail {

	matchingPods := common.FilterNamespacedPodsByLabelSelector(pods, petSet.ObjectMeta.Namespace,
		petSet.Spec.Selector)

	podInfo := common.GetPodInfo(int32(petSet.Status.Replicas), int32(petSet.Spec.Replicas),
		matchingPods)

	return PetSetDetail{
		ObjectMeta:      common.NewObjectMeta(petSet.ObjectMeta),
		TypeMeta:        common.NewTypeMeta(common.ResourceKindPetSet),
		ContainerImages: common.GetContainerImages(&petSet.Spec.Template.Spec),
		PodInfo:         podInfo,
		PodList:         pod.CreatePodList(matchingPods, heapsterClient),
		EventList:       *events,
	}
}
// GetReplicationControllerDetail returns detailed information about the given replication
// controller in the given namespace.
func GetReplicationControllerDetail(client k8sClient.Interface, heapsterClient client.HeapsterClient,
	namespace, name string) (*ReplicationControllerDetail, error) {
	log.Printf("Getting details of %s replication controller in %s namespace", name, namespace)

	replicationControllerWithPods, err := getRawReplicationControllerWithPods(client, namespace, name)
	if err != nil {
		return nil, err
	}
	replicationController := replicationControllerWithPods.ReplicationController
	pods := replicationControllerWithPods.Pods

	services, err := client.Services(namespace).List(api.ListOptions{
		LabelSelector: labels.Everything(),
		FieldSelector: fields.Everything(),
	})

	if err != nil {
		return nil, err
	}

	replicationControllerDetail := &ReplicationControllerDetail{
		ObjectMeta:    common.NewObjectMeta(replicationController.ObjectMeta),
		TypeMeta:      common.NewTypeMeta(common.ResourceKindReplicationController),
		LabelSelector: replicationController.Spec.Selector,
		PodInfo:       getReplicationPodInfo(replicationController, pods.Items),
		ServiceList:   resourceService.ServiceList{Services: make([]resourceService.Service, 0)},
	}

	matchingServices := getMatchingServices(services.Items, replicationController)

	for _, service := range matchingServices {
		replicationControllerDetail.ServiceList.Services = append(
			replicationControllerDetail.ServiceList.Services, resourceService.ToService(&service))
	}

	for _, container := range replicationController.Spec.Template.Spec.Containers {
		replicationControllerDetail.ContainerImages = append(replicationControllerDetail.ContainerImages,
			container.Image)
	}

	replicationControllerDetail.Pods = pod.CreatePodList(pods.Items, heapsterClient)

	return replicationControllerDetail, nil
}
// Returns detailed information about the given daemon set in the given namespace.
func GetDaemonSetDetail(client k8sClient.Interface, heapsterClient client.HeapsterClient,
	namespace, name string) (*DaemonSetDetail, error) {
	log.Printf("Getting details of %s daemon set in %s namespace", name, namespace)

	daemonSetWithPods, err := getRawDaemonSetWithPods(client, namespace, name)
	if err != nil {
		return nil, err
	}
	daemonSet := daemonSetWithPods.DaemonSet
	pods := daemonSetWithPods.Pods

	services, err := client.Services(namespace).List(api.ListOptions{
		LabelSelector: labels.Everything(),
		FieldSelector: fields.Everything(),
	})

	if err != nil {
		return nil, err
	}

	daemonSetDetail := &DaemonSetDetail{
		ObjectMeta:    common.NewObjectMeta(daemonSet.ObjectMeta),
		TypeMeta:      common.NewTypeMeta(common.ResourceKindDaemonSet),
		LabelSelector: daemonSet.Spec.Selector,
		PodInfo:       getDaemonSetPodInfo(daemonSet, pods.Items),
		ServiceList:   resourceService.ServiceList{Services: make([]resourceService.Service, 0)},
	}

	matchingServices := getMatchingServicesforDS(services.Items, daemonSet)

	for _, service := range matchingServices {
		daemonSetDetail.ServiceList.Services = append(daemonSetDetail.ServiceList.Services,
			resourceService.ToService(&service))
	}

	for _, container := range daemonSet.Spec.Template.Spec.Containers {
		daemonSetDetail.ContainerImages = append(daemonSetDetail.ContainerImages,
			container.Image)
	}

	daemonSetDetail.Pods = pod.CreatePodList(pods.Items, heapsterClient)

	return daemonSetDetail, nil
}
func toNodeDetail(node api.Node, pods pod.PodList, eventList common.EventList,
	allocatedResources NodeAllocatedResources) NodeDetail {

	return NodeDetail{
		ObjectMeta:         common.NewObjectMeta(node.ObjectMeta),
		TypeMeta:           common.NewTypeMeta(common.ResourceKindNode),
		Phase:              node.Status.Phase,
		ExternalID:         node.Spec.ExternalID,
		ProviderID:         node.Spec.ProviderID,
		PodCIDR:            node.Spec.PodCIDR,
		Unschedulable:      node.Spec.Unschedulable,
		NodeInfo:           node.Status.NodeInfo,
		Conditions:         node.Status.Conditions,
		ContainerImages:    getContainerImages(node),
		PodList:            pods,
		EventList:          eventList,
		AllocatedResources: allocatedResources,
	}
}
func getJobDetail(job *batch.Job, heapsterClient client.HeapsterClient,
	events *common.EventList, pods []api.Pod) JobDetail {

	matchingPods := common.FilterNamespacedPodsBySelector(pods, job.ObjectMeta.Namespace,
		job.Spec.Selector.MatchLabels)

	podInfo := getPodInfo(job, matchingPods)

	return JobDetail{
		ObjectMeta:      common.NewObjectMeta(job.ObjectMeta),
		TypeMeta:        common.NewTypeMeta(common.ResourceKindJob),
		ContainerImages: common.GetContainerImages(&job.Spec.Template.Spec),
		PodInfo:         podInfo,
		PodList:         pod.CreatePodList(matchingPods, heapsterClient),
		EventList:       *events,
		Parallelism:     job.Spec.Parallelism,
		Completions:     job.Spec.Completions,
	}
}
func getDeploymentDetail(deployment *extensions.Deployment,
	oldRs []*extensions.ReplicaSet, newRs *extensions.ReplicaSet,
	pods []api.Pod, events *common.EventList, rawEvents []api.Event) *DeploymentDetail {
	var newReplicaSet replicaset.ReplicaSet

	if newRs != nil {
		newRsPodInfo := common.GetPodInfo(newRs.Status.Replicas, newRs.Spec.Replicas, pods)
		newReplicaSet = replicaset.ToReplicaSet(newRs, &newRsPodInfo)
	}

	oldReplicaSets := make([]extensions.ReplicaSet, len(oldRs))
	for i, replicaSet := range oldRs {
		oldReplicaSets[i] = *replicaSet
	}
	oldReplicaSetList := replicaset.ToReplicaSetList(oldReplicaSets, pods, rawEvents)

	var rollingUpdateStrategy *RollingUpdateStrategy
	if deployment.Spec.Strategy.RollingUpdate != nil {
		rollingUpdateStrategy = &RollingUpdateStrategy{
			MaxSurge:       deployment.Spec.Strategy.RollingUpdate.MaxSurge.IntValue(),
			MaxUnavailable: deployment.Spec.Strategy.RollingUpdate.MaxUnavailable.IntValue(),
		}
	}

	return &DeploymentDetail{
		ObjectMeta:            common.NewObjectMeta(deployment.ObjectMeta),
		TypeMeta:              common.NewTypeMeta(common.ResourceKindDeployment),
		Selector:              deployment.Spec.Selector.MatchLabels,
		StatusInfo:            GetStatusInfo(&deployment.Status),
		Strategy:              deployment.Spec.Strategy.Type,
		MinReadySeconds:       deployment.Spec.MinReadySeconds,
		RollingUpdateStrategy: rollingUpdateStrategy,
		OldReplicaSetList:     *oldReplicaSetList,
		NewReplicaSet:         newReplicaSet,
		RevisionHistoryLimit:  deployment.Spec.RevisionHistoryLimit,
		EventList:             *events,
	}
}
func toNode(node api.Node) Node {
	return Node{
		ObjectMeta: common.NewObjectMeta(node.ObjectMeta),
		TypeMeta:   common.NewTypeMeta(common.ResourceKindNode),
	}
}
func TestGetDeploymentDetail(t *testing.T) {
	podList := &api.PodList{}
	eventList := &api.EventList{}

	deployment := &extensions.Deployment{
		ObjectMeta: api.ObjectMeta{
			Name:   "test-name",
			Labels: map[string]string{"track": "beta"},
		},
		Spec: extensions.DeploymentSpec{
			Selector:        &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
			Replicas:        4,
			MinReadySeconds: 5,
			Strategy: extensions.DeploymentStrategy{
				Type: extensions.RollingUpdateDeploymentStrategyType,
				RollingUpdate: &extensions.RollingUpdateDeployment{
					MaxSurge:       intstr.FromInt(1),
					MaxUnavailable: intstr.FromString("1"),
				},
			},
			Template: api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Name:   "test-pod-name",
					Labels: map[string]string{"track": "beta"},
				},
			},
		},
		Status: extensions.DeploymentStatus{
			Replicas:            4,
			UpdatedReplicas:     2,
			AvailableReplicas:   3,
			UnavailableReplicas: 1,
		},
	}

	podTemplateSpec := deploymentutil.GetNewReplicaSetTemplate(deployment)

	newReplicaSet := extensions.ReplicaSet{
		ObjectMeta: api.ObjectMeta{
			Name:      "replica-set-1",
			Namespace: "test-namespace",
		},
		Spec: extensions.ReplicaSetSpec{
			Template: podTemplateSpec,
		},
	}

	replicaSetList := &extensions.ReplicaSetList{
		Items: []extensions.ReplicaSet{
			newReplicaSet,
			{
				ObjectMeta: api.ObjectMeta{
					Name:      "replica-set-2",
					Namespace: "test-namespace",
				},
			},
		},
	}

	cases := []struct {
		namespace, name string
		expectedActions []string
		deployment      *extensions.Deployment
		expected        *DeploymentDetail
	}{
		{
			"test-namespace", "test-name",
			[]string{"get", "list", "list", "list"},
			deployment,
			&DeploymentDetail{
				ObjectMeta: common.ObjectMeta{
					Name:   "test-name",
					Labels: map[string]string{"track": "beta"},
				},
				TypeMeta: common.TypeMeta{Kind: common.ResourceKindDeployment},
				Selector: map[string]string{"foo": "bar"},
				StatusInfo: StatusInfo{
					Replicas:    4,
					Updated:     2,
					Available:   3,
					Unavailable: 1,
				},
				Strategy:        "RollingUpdate",
				MinReadySeconds: 5,
				RollingUpdateStrategy: &RollingUpdateStrategy{
					MaxSurge:       1,
					MaxUnavailable: 1,
				},
				OldReplicaSetList: replicaset.ReplicaSetList{ReplicaSets: []replicaset.ReplicaSet{}},
				NewReplicaSet: replicaset.ReplicaSet{
					ObjectMeta: common.NewObjectMeta(newReplicaSet.ObjectMeta),
					TypeMeta:   common.NewTypeMeta(common.ResourceKindReplicaSet),
					Pods:       common.PodInfo{Warnings: []common.Event{}},
				},
				EventList: common.EventList{
					Namespace: "test-namespace",
					Events:    []common.Event{},
				},
			},
		},
	}

	for _, c := range cases {

		fakeClient := testclient.NewSimpleFake(c.deployment, replicaSetList, podList, eventList)

		actual, _ := GetDeploymentDetail(fakeClient, c.namespace, c.name)

		actions := fakeClient.Actions()
		if len(actions) != len(c.expectedActions) {
			t.Errorf("Unexpected actions: %v, expected %d actions got %d", actions,
				len(c.expectedActions), len(actions))
			continue
		}

		for i, verb := range c.expectedActions {
			if actions[i].GetVerb() != verb {
				t.Errorf("Unexpected action: %+v, expected %s",
					actions[i], verb)
			}
		}

		if !reflect.DeepEqual(actual, c.expected) {
			t.Errorf("GetDeploymentDetail(client, namespace, name) == \ngot: %#v, \nexpected %#v",
				actual, c.expected)
		}
	}
}