package network

import (
	"errors"
	"fmt"
	"strings"

	kapi "k8s.io/kubernetes/pkg/api"
	kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	kcontainer "k8s.io/kubernetes/pkg/kubelet/container"
	kexec "k8s.io/kubernetes/pkg/util/exec"

	"github.com/openshift/origin/pkg/diagnostics/networkpod/util"
	"github.com/openshift/origin/pkg/diagnostics/types"
)

const (
	CheckNodeNetworkName = "CheckNodeNetwork"
)

// CheckNodeNetwork is a Diagnostic to check that pods in the cluster can access its own node
type CheckNodeNetwork struct {
	KubeClient *kclientset.Clientset
}

// Name is part of the Diagnostic interface and just returns name.
func (d CheckNodeNetwork) Name() string {
	return CheckNodeNetworkName
}

// Description is part of the Diagnostic interface and just returns the diagnostic description.
func (d CheckNodeNetwork) Description() string {
	return "Check that pods in the cluster can access its own node."
}

// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d CheckNodeNetwork) CanRun() (bool, error) {
	if d.KubeClient == nil {
		return false, errors.New("must have kube client")
	}
	return true, nil
}

// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d CheckNodeNetwork) Check() types.DiagnosticResult {
	r := types.NewDiagnosticResult(CheckNodeNetworkName)

	_, localIP, err := util.GetLocalNode(d.KubeClient)
	if err != nil {
		r.Error("DNodeNet1001", err, err.Error())
		return r
	}

	localPods, _, err := util.GetLocalAndNonLocalDiagnosticPods(d.KubeClient)
	if err != nil {
		r.Error("DNodeNet1002", err, fmt.Sprintf("Getting local and nonlocal pods failed. Error: %s", err))
		return r
	}

	for _, pod := range localPods {
		checkNodeConnection(&pod, localIP, r)
	}
	return r
}

func checkNodeConnection(pod *kapi.Pod, nodeIP string, r types.DiagnosticResult) {
	if len(pod.Status.ContainerStatuses) == 0 {
		err := fmt.Errorf("ContainerID not found for pod %q", util.PrintPod(pod))
		r.Error("DNodeNet1003", err, err.Error())
		return
	}

	kexecer := kexec.New()
	containerID := kcontainer.ParseContainerID(pod.Status.ContainerStatuses[0].ContainerID).ID
	pid, err := kexecer.Command("docker", "inspect", "-f", "{{.State.Pid}}", containerID).CombinedOutput()
	if err != nil {
		r.Error("DNodeNet1004", err, fmt.Sprintf("Fetching pid for pod %q, container %q failed. Error: %s", util.PrintPod(pod), containerID, err))
		return
	}

	if _, err := kexecer.Command("nsenter", "-n", "-t", strings.Trim(fmt.Sprintf("%s", pid), "\n"), "--", "ping", "-c1", "-W2", nodeIP).CombinedOutput(); err != nil {
		r.Error("DNodeNet1005", err, fmt.Sprintf("Connectivity from pod %q to node %q failed. Error: %s", util.PrintPod(pod), nodeIP, err))
	}
}