Add replication controller stats in CLI (#794)

* Add replication controller stats in CLI
* Fix pod status in stat summary tests

Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
This commit is contained in:
Kevin Lingerfelt 2018-04-18 18:12:14 -07:00 committed by GitHub
parent 06dd8d90ee
commit 653dc6bfaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 32 deletions

View File

@ -34,14 +34,15 @@ Valid resource types include:
* deployments * deployments
* namespaces * namespaces
* pods * pods
* replicationcontrollers
This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases. This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases.
If no resource name is specified, displays stats about all resources of the specified RESOURCETYPE`, If no resource name is specified, displays stats about all resources of the specified RESOURCETYPE`,
Example: ` # Get all deployments in the test namespace. Example: ` # Get all deployments in the test namespace.
conduit stat deployments -n test conduit stat deployments -n test
# Get the hello1 deployment in the test namespace. # Get the hello1 replication controller in the test namespace.
conduit stat deployments hello1 -n test conduit stat replicationcontrollers hello1 -n test
# Get all namespaces. # Get all namespaces.
conduit stat namespaces conduit stat namespaces
@ -49,7 +50,12 @@ If no resource name is specified, displays stats about all resources of the spec
# Get all pods in all namespaces that call the hello1 deployment in the test namesapce. # Get all pods in all namespaces that call the hello1 deployment in the test namesapce.
conduit stat pods --to hello1 --to-resource deployment --to-namespace test --all-namespaces`, conduit stat pods --to hello1 --to-resource deployment --to-namespace test --all-namespaces`,
Args: cobra.RangeArgs(1, 2), Args: cobra.RangeArgs(1, 2),
ValidArgs: []string{k8s.KubernetesDeployments, k8s.KubernetesNamespaces, k8s.KubernetesPods}, ValidArgs: []string{
k8s.KubernetesDeployments,
k8s.KubernetesNamespaces,
k8s.KubernetesPods,
k8s.KubernetesReplicationControllers,
},
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
switch len(args) { switch len(args) {
case 1: case 1:

View File

@ -23,7 +23,7 @@ rules:
resources: ["deployments", "replicasets"] resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
- apiGroups: [""] - apiGroups: [""]
resources: ["pods", "endpoints", "services", "namespaces"] resources: ["pods", "endpoints", "services", "namespaces", "replicationcontrollers"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
--- ---

View File

@ -23,7 +23,7 @@ rules:
resources: ["deployments", "replicasets"] resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
- apiGroups: [""] - apiGroups: [""]
resources: ["pods", "endpoints", "services", "namespaces"] resources: ["pods", "endpoints", "services", "namespaces", "replicationcontrollers"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
--- ---

View File

@ -26,7 +26,7 @@ rules:
resources: ["deployments", "replicasets"] resources: ["deployments", "replicasets"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
- apiGroups: [""] - apiGroups: [""]
resources: ["pods", "endpoints", "services", "namespaces"] resources: ["pods", "endpoints", "services", "namespaces", "replicationcontrollers"]
verbs: ["list", "get", "watch"] verbs: ["list", "get", "watch"]
--- ---

View File

@ -28,13 +28,14 @@ type (
deployLister applisters.DeploymentLister deployLister applisters.DeploymentLister
replicaSetLister applisters.ReplicaSetLister replicaSetLister applisters.ReplicaSetLister
podLister corelisters.PodLister podLister corelisters.PodLister
replicationControllerLister corelisters.ReplicationControllerLister
controllerNamespace string controllerNamespace string
ignoredNamespaces []string ignoredNamespaces []string
} }
) )
const ( const (
podQuery = "sum(request_total) by (pod)" podQuery = "count(process_start_time_seconds) by (pod)"
K8sClientSubsystemName = "kubernetes" K8sClientSubsystemName = "kubernetes"
K8sClientCheckDescription = "control plane can talk to Kubernetes" K8sClientCheckDescription = "control plane can talk to Kubernetes"
PromClientSubsystemName = "prometheus" PromClientSubsystemName = "prometheus"
@ -48,6 +49,7 @@ func newGrpcServer(
deployLister applisters.DeploymentLister, deployLister applisters.DeploymentLister,
replicaSetLister applisters.ReplicaSetLister, replicaSetLister applisters.ReplicaSetLister,
podLister corelisters.PodLister, podLister corelisters.PodLister,
replicationControllerLister corelisters.ReplicationControllerLister,
controllerNamespace string, controllerNamespace string,
ignoredNamespaces []string, ignoredNamespaces []string,
) *grpcServer { ) *grpcServer {
@ -58,6 +60,7 @@ func newGrpcServer(
deployLister: deployLister, deployLister: deployLister,
replicaSetLister: replicaSetLister, replicaSetLister: replicaSetLister,
podLister: podLister, podLister: podLister,
replicationControllerLister: replicationControllerLister,
controllerNamespace: controllerNamespace, controllerNamespace: controllerNamespace,
ignoredNamespaces: ignoredNamespaces, ignoredNamespaces: ignoredNamespaces,
} }

View File

@ -170,6 +170,7 @@ spec:
deployInformer := sharedInformers.Apps().V1beta2().Deployments() deployInformer := sharedInformers.Apps().V1beta2().Deployments()
replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets() replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets()
podInformer := sharedInformers.Core().V1().Pods() podInformer := sharedInformers.Core().V1().Pods()
replicationControllerInformer := sharedInformers.Core().V1().ReplicationControllers()
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes}, &MockProm{Res: exp.promRes},
@ -178,6 +179,7 @@ spec:
deployInformer.Lister(), deployInformer.Lister(),
replicaSetInformer.Lister(), replicaSetInformer.Lister(),
podInformer.Lister(), podInformer.Lister(),
replicationControllerInformer.Lister(),
"conduit", "conduit",
[]string{}, []string{},
) )
@ -189,6 +191,7 @@ spec:
deployInformer.Informer().HasSynced, deployInformer.Informer().HasSynced,
replicaSetInformer.Informer().HasSynced, replicaSetInformer.Informer().HasSynced,
podInformer.Informer().HasSynced, podInformer.Informer().HasSynced,
replicationControllerInformer.Informer().HasSynced,
) { ) {
t.Fatalf("timed out wait for caches to sync") t.Fatalf("timed out wait for caches to sync")
} }

View File

@ -200,6 +200,7 @@ func NewServer(
deployLister applisters.DeploymentLister, deployLister applisters.DeploymentLister,
replicaSetLister applisters.ReplicaSetLister, replicaSetLister applisters.ReplicaSetLister,
podLister corelisters.PodLister, podLister corelisters.PodLister,
replicationControllerLister corelisters.ReplicationControllerLister,
controllerNamespace string, controllerNamespace string,
ignoredNamespaces []string, ignoredNamespaces []string,
) *http.Server { ) *http.Server {
@ -211,6 +212,7 @@ func NewServer(
deployLister, deployLister,
replicaSetLister, replicaSetLister,
podLister, podLister,
replicationControllerLister,
controllerNamespace, controllerNamespace,
ignoredNamespaces, ignoredNamespaces,
), ),

View File

@ -35,7 +35,11 @@ const (
promLatencyP95 = promType("0.95") promLatencyP95 = promType("0.95")
promLatencyP99 = promType("0.99") promLatencyP99 = promType("0.99")
deploymentLabel = model.LabelName("deployment")
namespaceLabel = model.LabelName("namespace") namespaceLabel = model.LabelName("namespace")
podLabel = model.LabelName("pod")
replicationControllerLabel = model.LabelName("replication_controller")
dstNamespaceLabel = model.LabelName("dst_namespace") dstNamespaceLabel = model.LabelName("dst_namespace")
) )
@ -43,9 +47,10 @@ var (
promTypes = []promType{promRequests, promLatencyP50, promLatencyP95, promLatencyP99} promTypes = []promType{promRequests, promLatencyP50, promLatencyP95, promLatencyP99}
k8sResourceTypesToPromLabels = map[string]model.LabelName{ k8sResourceTypesToPromLabels = map[string]model.LabelName{
k8s.KubernetesDeployments: "deployment", k8s.KubernetesDeployments: deploymentLabel,
k8s.KubernetesNamespaces: namespaceLabel, k8s.KubernetesNamespaces: namespaceLabel,
k8s.KubernetesPods: "pod", k8s.KubernetesPods: podLabel,
k8s.KubernetesReplicationControllers: replicationControllerLabel,
} }
) )
@ -66,6 +71,8 @@ func (s *grpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest
objectMap, meshCount, err = s.getNamespaces(req.Selector.Resource) objectMap, meshCount, err = s.getNamespaces(req.Selector.Resource)
case k8s.KubernetesPods: case k8s.KubernetesPods:
objectMap, meshCount, err = s.getPods(req.Selector.Resource) objectMap, meshCount, err = s.getPods(req.Selector.Resource)
case k8s.KubernetesReplicationControllers:
objectMap, meshCount, err = s.getReplicationControllers(req.Selector.Resource)
default: default:
err = fmt.Errorf("Unimplemented resource type: %v", req.Selector.Resource.Type) err = fmt.Errorf("Unimplemented resource type: %v", req.Selector.Resource.Type)
} }
@ -405,7 +412,7 @@ func (s *grpcServer) getPods(res *pb.Resource) (map[string]metav1.ObjectMeta, ma
meshedPodCount := make(map[string]*meshedCount) meshedPodCount := make(map[string]*meshedCount)
podMap := make(map[string]metav1.ObjectMeta) podMap := make(map[string]metav1.ObjectMeta)
for _, pod := range pods { for _, pod := range pods {
if pod.Status.Phase != apiv1.PodRunning { if !isPendingOrRunning(pod) {
continue continue
} }
@ -425,6 +432,43 @@ func (s *grpcServer) getPods(res *pb.Resource) (map[string]metav1.ObjectMeta, ma
return podMap, meshedPodCount, nil return podMap, meshedPodCount, nil
} }
func (s *grpcServer) getReplicationControllers(res *pb.Resource) (map[string]metav1.ObjectMeta, map[string]*meshedCount, error) {
var err error
var rcs []*apiv1.ReplicationController
if res.Namespace == "" {
rcs, err = s.replicationControllerLister.List(labels.Everything())
} else if res.Name == "" {
rcs, err = s.replicationControllerLister.ReplicationControllers(res.Namespace).List(labels.Everything())
} else {
var rc *apiv1.ReplicationController
rc, err = s.replicationControllerLister.ReplicationControllers(res.Namespace).Get(res.Name)
rcs = []*apiv1.ReplicationController{rc}
}
if err != nil {
return nil, nil, err
}
meshedPodCount := make(map[string]*meshedCount)
rcMap := make(map[string]metav1.ObjectMeta)
for _, rc := range rcs {
key, err := cache.MetaNamespaceKeyFunc(rc)
if err != nil {
return nil, nil, err
}
rcMap[key] = rc.ObjectMeta
meshCount, err := s.getMeshedPodCount(rc.Namespace, rc)
if err != nil {
return nil, nil, err
}
meshedPodCount[key] = meshCount
}
return rcMap, meshedPodCount, nil
}
func (s *grpcServer) getMeshedPodCount(namespace string, obj runtime.Object) (*meshedCount, error) { func (s *grpcServer) getMeshedPodCount(namespace string, obj runtime.Object) (*meshedCount, error) {
selector, err := getSelectorFromObject(obj) selector, err := getSelectorFromObject(obj)
if err != nil { if err != nil {
@ -438,6 +482,10 @@ func (s *grpcServer) getMeshedPodCount(namespace string, obj runtime.Object) (*m
meshCount := &meshedCount{} meshCount := &meshedCount{}
for _, pod := range pods { for _, pod := range pods {
if !isPendingOrRunning(pod) {
continue
}
meshCount.total++ meshCount.total++
if isInMesh(pod) { if isInMesh(pod) {
meshCount.inMesh++ meshCount.inMesh++
@ -452,6 +500,13 @@ func isInMesh(pod *apiv1.Pod) bool {
return ok return ok
} }
func isPendingOrRunning(pod *apiv1.Pod) bool {
pending := pod.Status.Phase == apiv1.PodPending
running := pod.Status.Phase == apiv1.PodRunning
terminating := pod.DeletionTimestamp != nil
return (pending || running) && !terminating
}
func getSelectorFromObject(obj runtime.Object) (labels.Selector, error) { func getSelectorFromObject(obj runtime.Object) (labels.Selector, error) {
switch typed := obj.(type) { switch typed := obj.(type) {
case *apiv1.Namespace: case *apiv1.Namespace:
@ -460,6 +515,9 @@ func getSelectorFromObject(obj runtime.Object) (labels.Selector, error) {
case *appsv1beta2.Deployment: case *appsv1beta2.Deployment:
return labels.Set(typed.Spec.Selector.MatchLabels).AsSelector(), nil return labels.Set(typed.Spec.Selector.MatchLabels).AsSelector(), nil
case *apiv1.ReplicationController:
return labels.Set(typed.Spec.Selector).AsSelector(), nil
default: default:
return nil, fmt.Errorf("Cannot get object selector: %v", obj) return nil, fmt.Errorf("Cannot get object selector: %v", obj)
} }

View File

@ -56,6 +56,8 @@ metadata:
app: emoji-svc app: emoji-svc
annotations: annotations:
conduit.io/proxy-version: testinjectversion conduit.io/proxy-version: testinjectversion
status:
phase: Running
`, ` `, `
apiVersion: v1 apiVersion: v1
kind: Pod kind: Pod
@ -64,6 +66,20 @@ metadata:
namespace: emojivoto namespace: emojivoto
labels: labels:
app: emoji-svc app: emoji-svc
status:
phase: Running
`, `
apiVersion: v1
kind: Pod
metadata:
name: emojivoto-meshed-not-running
namespace: emojivoto
labels:
app: emoji-svc
annotations:
conduit.io/proxy-version: testinjectversion
status:
phase: Completed
`, `,
}, },
promRes: model.Vector{ promRes: model.Vector{
@ -140,6 +156,7 @@ metadata:
deployInformer := sharedInformers.Apps().V1beta2().Deployments() deployInformer := sharedInformers.Apps().V1beta2().Deployments()
replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets() replicaSetInformer := sharedInformers.Apps().V1beta2().ReplicaSets()
podInformer := sharedInformers.Core().V1().Pods() podInformer := sharedInformers.Core().V1().Pods()
replicationControllerInformer := sharedInformers.Core().V1().ReplicationControllers()
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes}, &MockProm{Res: exp.promRes},
@ -148,6 +165,7 @@ metadata:
deployInformer.Lister(), deployInformer.Lister(),
replicaSetInformer.Lister(), replicaSetInformer.Lister(),
podInformer.Lister(), podInformer.Lister(),
replicationControllerInformer.Lister(),
"conduit", "conduit",
[]string{}, []string{},
) )
@ -159,6 +177,7 @@ metadata:
deployInformer.Informer().HasSynced, deployInformer.Informer().HasSynced,
replicaSetInformer.Informer().HasSynced, replicaSetInformer.Informer().HasSynced,
podInformer.Informer().HasSynced, podInformer.Informer().HasSynced,
replicationControllerInformer.Informer().HasSynced,
) { ) {
t.Fatalf("timed out wait for caches to sync") t.Fatalf("timed out wait for caches to sync")
} }
@ -218,6 +237,7 @@ metadata:
sharedInformers.Apps().V1beta2().Deployments().Lister(), sharedInformers.Apps().V1beta2().Deployments().Lister(),
sharedInformers.Apps().V1beta2().ReplicaSets().Lister(), sharedInformers.Apps().V1beta2().ReplicaSets().Lister(),
sharedInformers.Core().V1().Pods().Lister(), sharedInformers.Core().V1().Pods().Lister(),
sharedInformers.Core().V1().ReplicationControllers().Lister(),
"conduit", "conduit",
[]string{}, []string{},
) )

View File

@ -69,6 +69,9 @@ func main() {
podInformer := sharedInformers.Core().V1().Pods() podInformer := sharedInformers.Core().V1().Pods()
podInformerSynced := podInformer.Informer().HasSynced podInformerSynced := podInformer.Informer().HasSynced
replicationControllerInformer := sharedInformers.Core().V1().ReplicationControllers()
replicationControllerInformerSynced := replicationControllerInformer.Informer().HasSynced
sharedInformers.Start(nil) sharedInformers.Start(nil)
prometheusClient, err := promApi.NewClient(promApi.Config{Address: *prometheusUrl}) prometheusClient, err := promApi.NewClient(promApi.Config{Address: *prometheusUrl})
@ -84,6 +87,7 @@ func main() {
deployInformer.Lister(), deployInformer.Lister(),
replicaSetInformer.Lister(), replicaSetInformer.Lister(),
podInformer.Lister(), podInformer.Lister(),
replicationControllerInformer.Lister(),
*controllerNamespace, *controllerNamespace,
strings.Split(*ignoredNamespaces, ","), strings.Split(*ignoredNamespaces, ","),
) )
@ -99,6 +103,7 @@ func main() {
deployInformerSynced, deployInformerSynced,
replicaSetInformerSynced, replicaSetInformerSynced,
podInformerSynced, podInformerSynced,
replicationControllerInformerSynced,
) { ) {
log.Fatalf("timed out wait for caches to sync") log.Fatalf("timed out wait for caches to sync")
} }

View File

@ -12,6 +12,7 @@ const (
KubernetesDeployments = "deployments" KubernetesDeployments = "deployments"
KubernetesNamespaces = "namespaces" KubernetesNamespaces = "namespaces"
KubernetesPods = "pods" KubernetesPods = "pods"
KubernetesReplicationControllers = "replicationcontrollers"
) )
func generateKubernetesApiBaseUrlFor(schemeHostAndPort string, namespace string, extraPathStartingWithSlash string) (*url.URL, error) { func generateKubernetesApiBaseUrlFor(schemeHostAndPort string, namespace string, extraPathStartingWithSlash string) (*url.URL, error) {
@ -63,6 +64,8 @@ func CanonicalKubernetesNameFromFriendlyName(friendlyName string) (string, error
return KubernetesNamespaces, nil return KubernetesNamespaces, nil
case "po", "pod", "pods": case "po", "pod", "pods":
return KubernetesPods, nil return KubernetesPods, nil
case "rc", "replicationcontroller", "replicationcontrollers":
return KubernetesReplicationControllers, nil
} }
return "", fmt.Errorf("cannot find Kubernetes canonical name from friendly name [%s]", friendlyName) return "", fmt.Errorf("cannot find Kubernetes canonical name from friendly name [%s]", friendlyName)