mirror of https://github.com/linkerd/linkerd2.git
DaemonSet stats are not currently shown in the cli stat command, web ui or grafana dashboard. This commit adds daemonset support for stat. Update stat command's help message to reference daemonsets. Update the public-api to support stats for daemonsets. Add tests for stat summary and api. Add daemonset get/list/watch permissions to the linkerd-controller cluster role that's created using the install command. Update golden expectation test files for install command yaml manifest output. Update web UI with daemonsets Update navigation, overview and pages to list daemonsets and the pods associated to them. Add daemonset paths to server, and ui apps. Add grafana dashboard for daemonsets; a clone of the deployment dashboard. Update dependencies and dockerfile hashes Add DaemonSet support to tap and top commands Fixes of #2006 Signed-off-by: Zak Knill <zrjknill@gmail.com>
This commit is contained in:
parent
2217a6cd2b
commit
8c413ca38b
|
@ -1052,6 +1052,7 @@
|
|||
"k8s.io/client-go/discovery/fake",
|
||||
"k8s.io/client-go/informers",
|
||||
"k8s.io/client-go/informers/admissionregistration/v1beta1",
|
||||
"k8s.io/client-go/informers/apps/v1",
|
||||
"k8s.io/client-go/informers/apps/v1beta2",
|
||||
"k8s.io/client-go/informers/core/v1",
|
||||
"k8s.io/client-go/kubernetes",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
## compile binaries
|
||||
FROM gcr.io/linkerd-io/go-deps:8c5ab859 as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:02bd701f as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY cli cli
|
||||
COPY controller/k8s controller/k8s
|
||||
|
|
|
@ -59,6 +59,7 @@ func newCmdStat() *cobra.Command {
|
|||
Examples:
|
||||
* deploy
|
||||
* deploy/my-deploy
|
||||
* ds/my-daemonset
|
||||
* rc/my-replication-controller
|
||||
* ns/my-ns
|
||||
* authority
|
||||
|
@ -69,6 +70,7 @@ func newCmdStat() *cobra.Command {
|
|||
* all
|
||||
|
||||
Valid resource types include:
|
||||
* daemonsets
|
||||
* deployments
|
||||
* namespaces
|
||||
* pods
|
||||
|
|
|
@ -58,9 +58,11 @@ func newCmdTap() *cobra.Command {
|
|||
* deploy
|
||||
* deploy/my-deploy
|
||||
* deploy my-deploy
|
||||
* ds/my-daemonset
|
||||
* ns/my-ns
|
||||
|
||||
Valid resource types include:
|
||||
* daemonsets
|
||||
* deployments
|
||||
* namespaces
|
||||
* pods
|
||||
|
|
|
@ -20,7 +20,7 @@ metadata:
|
|||
name: linkerd-linkerd-controller
|
||||
rules:
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments", "replicasets"]
|
||||
resources: ["daemonsets", "deployments", "replicasets"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"]
|
||||
|
|
|
@ -20,7 +20,7 @@ metadata:
|
|||
name: linkerd-linkerd-controller
|
||||
rules:
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments", "replicasets"]
|
||||
resources: ["daemonsets", "deployments", "replicasets"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"]
|
||||
|
|
|
@ -20,7 +20,7 @@ metadata:
|
|||
name: linkerd-linkerd-controller
|
||||
rules:
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments", "replicasets"]
|
||||
resources: ["daemonsets", "deployments", "replicasets"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"]
|
||||
|
|
|
@ -22,7 +22,7 @@ metadata:
|
|||
name: linkerd-Namespace-controller
|
||||
rules:
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments", "replicasets"]
|
||||
resources: ["daemonsets", "deployments", "replicasets"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "endpoints", "services", "replicationcontrollers", "namespaces"]
|
||||
|
|
|
@ -15,7 +15,7 @@ metadata:
|
|||
namespace: Namespace
|
||||
rules:
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments", "replicasets"]
|
||||
resources: ["daemonsets", "deployments", "replicasets"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "endpoints", "services", "replicationcontrollers"]
|
||||
|
|
|
@ -284,9 +284,11 @@ func newCmdTop() *cobra.Command {
|
|||
* deploy
|
||||
* deploy/my-deploy
|
||||
* deploy my-deploy
|
||||
* ds/my-daemonset
|
||||
* ns/my-ns
|
||||
|
||||
Valid resource types include:
|
||||
* daemonsets
|
||||
* deployments
|
||||
* namespaces
|
||||
* pods
|
||||
|
|
|
@ -32,7 +32,7 @@ metadata:
|
|||
{{- end}}
|
||||
rules:
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments", "replicasets"]
|
||||
resources: ["daemonsets", "deployments", "replicasets"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "endpoints", "services", "replicationcontrollers"{{if not .SingleNamespace}}, "namespaces"{{end}}]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
## compile controller services
|
||||
FROM gcr.io/linkerd-io/go-deps:8c5ab859 as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:02bd701f as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY controller/gen controller/gen
|
||||
COPY pkg pkg
|
||||
|
|
|
@ -81,6 +81,7 @@ func testStatSummary(t *testing.T, expectations []statSumExpected) {
|
|||
}
|
||||
|
||||
rspStatTables := rsp.GetOk().StatTables
|
||||
sort.Sort(byStatResult(rspStatTables))
|
||||
|
||||
if len(rspStatTables) != len(exp.expectedResponse.GetOk().StatTables) {
|
||||
t.Fatalf(
|
||||
|
@ -92,7 +93,6 @@ func testStatSummary(t *testing.T, expectations []statSumExpected) {
|
|||
)
|
||||
}
|
||||
|
||||
sort.Sort(byStatResult(rspStatTables))
|
||||
statOkRsp := &pb.StatSummaryResponse_Ok{
|
||||
StatTables: rspStatTables,
|
||||
}
|
||||
|
@ -209,6 +209,82 @@ status:
|
|||
testStatSummary(t, expectations)
|
||||
})
|
||||
|
||||
t.Run("Successfully performs a query based on resource type DaemonSet", func(t *testing.T) {
|
||||
expectations := []statSumExpected{
|
||||
statSumExpected{
|
||||
expectedStatRPC: expectedStatRPC{
|
||||
err: nil,
|
||||
k8sConfigs: []string{`
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: emoji
|
||||
namespace: emojivoto
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: emoji-svc
|
||||
strategy: {}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: buoyantio/emojivoto-emoji-svc:v3
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: emojivoto-meshed
|
||||
namespace: emojivoto
|
||||
labels:
|
||||
app: emoji-svc
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
status:
|
||||
phase: Running
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: emojivoto-not-meshed
|
||||
namespace: emojivoto
|
||||
labels:
|
||||
app: emoji-svc
|
||||
status:
|
||||
phase: Running
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: emojivoto-meshed-not-running
|
||||
namespace: emojivoto
|
||||
labels:
|
||||
app: emoji-svc
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
status:
|
||||
phase: Completed
|
||||
`,
|
||||
},
|
||||
mockPromResponse: prometheusMetric("emoji", "daemonset", "emojivoto", "success", false),
|
||||
},
|
||||
req: pb.StatSummaryRequest{
|
||||
Selector: &pb.ResourceSelection{
|
||||
Resource: &pb.Resource{
|
||||
Namespace: "emojivoto",
|
||||
Type: pkgK8s.DaemonSet,
|
||||
},
|
||||
},
|
||||
TimeWindow: "1m",
|
||||
},
|
||||
expectedResponse: GenStatSummaryResponse("emoji", pkgK8s.DaemonSet, []string{"emojivoto"}, &PodCounts{
|
||||
MeshedPods: 1,
|
||||
RunningPods: 2,
|
||||
FailedPods: 0,
|
||||
}, true),
|
||||
},
|
||||
}
|
||||
|
||||
testStatSummary(t, expectations)
|
||||
})
|
||||
|
||||
t.Run("Queries prometheus for a specific resource if name is specified", func(t *testing.T) {
|
||||
expectations := []statSumExpected{
|
||||
statSumExpected{
|
||||
|
@ -629,6 +705,13 @@ status:
|
|||
},
|
||||
},
|
||||
},
|
||||
&pb.StatTable{
|
||||
Table: &pb.StatTable_PodGroup_{
|
||||
PodGroup: &pb.StatTable_PodGroup{
|
||||
Rows: []*pb.StatTable_PodGroup_Row{},
|
||||
},
|
||||
},
|
||||
},
|
||||
&pb.StatTable{
|
||||
Table: &pb.StatTable_PodGroup_{
|
||||
PodGroup: &pb.StatTable_PodGroup{
|
||||
|
|
|
@ -2,6 +2,7 @@ package public
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
|
@ -11,9 +12,8 @@ import (
|
|||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
var booksConfig = []string{
|
||||
// deployment/books
|
||||
`kind: Deployment
|
||||
// deployment/books
|
||||
var booksDeployConfig = `kind: Deployment
|
||||
apiVersion: apps/v1beta2
|
||||
metadata:
|
||||
name: books
|
||||
|
@ -30,8 +30,28 @@ spec:
|
|||
spec:
|
||||
dnsPolicy: ClusterFirst
|
||||
containers:
|
||||
- image: buoyantio/booksapp:v0.0.2`,
|
||||
- image: buoyantio/booksapp:v0.0.2`
|
||||
|
||||
// daemonset/books
|
||||
var booksDaemonsetConfig = `kind: DaemonSet
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: books
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: books
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: books
|
||||
spec:
|
||||
dnsPolicy: ClusterFirst
|
||||
containers:
|
||||
- image: buoyantio/booksapp:v0.0.2`
|
||||
|
||||
var booksServiceConfig = []string{
|
||||
// service/books
|
||||
`apiVersion: v1
|
||||
kind: Service
|
||||
|
@ -71,6 +91,9 @@ spec:
|
|||
`,
|
||||
}
|
||||
|
||||
var booksConfig = append(booksServiceConfig, booksDeployConfig)
|
||||
var booksDSConfig = append(booksServiceConfig, booksDaemonsetConfig)
|
||||
|
||||
type topRoutesExpected struct {
|
||||
expectedStatRPC
|
||||
req pb.TopRoutesRequest // the request we would like to test
|
||||
|
@ -120,45 +143,46 @@ func genDefaultRouteSample() *model.Sample {
|
|||
}
|
||||
|
||||
func testTopRoutes(t *testing.T, expectations []topRoutesExpected) {
|
||||
for _, exp := range expectations {
|
||||
|
||||
mockProm, fakeGrpcServer, err := newMockGrpcServer(exp.expectedStatRPC)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating mock grpc server: %s", err)
|
||||
}
|
||||
|
||||
rsp, err := fakeGrpcServer.TopRoutes(context.TODO(), &exp.req)
|
||||
if err != exp.err {
|
||||
t.Fatalf("Expected error: %s, Got: %s", exp.err, err)
|
||||
}
|
||||
|
||||
err = exp.verifyPromQueries(mockProm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rows := rsp.GetOk().GetRoutes()[0].Rows
|
||||
|
||||
if len(rows) != len(exp.expectedResponse.GetOk().GetRoutes()[0].Rows) {
|
||||
t.Fatalf(
|
||||
"Expected [%d] rows, got [%d].\nExpected:\n%s\nGot:\n%s",
|
||||
len(exp.expectedResponse.GetOk().GetRoutes()[0].Rows),
|
||||
len(rows),
|
||||
exp.expectedResponse.GetOk().GetRoutes()[0].Rows,
|
||||
rows,
|
||||
)
|
||||
}
|
||||
|
||||
sort.Slice(rows, func(i, j int) bool {
|
||||
return rows[i].GetAuthority()+rows[i].GetRoute() < rows[j].GetAuthority()+rows[j].GetRoute()
|
||||
})
|
||||
|
||||
for i, row := range rows {
|
||||
expected := exp.expectedResponse.GetOk().GetRoutes()[0].Rows[i]
|
||||
if !proto.Equal(row, expected) {
|
||||
t.Fatalf("Expected: %+v\n Got: %+v", expected, row)
|
||||
for id, exp := range expectations {
|
||||
t.Run(fmt.Sprintf("%d", id), func(t *testing.T) {
|
||||
mockProm, fakeGrpcServer, err := newMockGrpcServer(exp.expectedStatRPC)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating mock grpc server: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
rsp, err := fakeGrpcServer.TopRoutes(context.TODO(), &exp.req)
|
||||
if err != exp.err {
|
||||
t.Fatalf("Expected error: %s, Got: %s", exp.err, err)
|
||||
}
|
||||
|
||||
err = exp.verifyPromQueries(mockProm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rows := rsp.GetOk().GetRoutes()[0].Rows
|
||||
|
||||
if len(rows) != len(exp.expectedResponse.GetOk().GetRoutes()[0].Rows) {
|
||||
t.Fatalf(
|
||||
"Expected [%d] rows, got [%d].\nExpected:\n%s\nGot:\n%s",
|
||||
len(exp.expectedResponse.GetOk().GetRoutes()[0].Rows),
|
||||
len(rows),
|
||||
exp.expectedResponse.GetOk().GetRoutes()[0].Rows,
|
||||
rows,
|
||||
)
|
||||
}
|
||||
|
||||
sort.Slice(rows, func(i, j int) bool {
|
||||
return rows[i].GetAuthority()+rows[i].GetRoute() < rows[j].GetAuthority()+rows[j].GetRoute()
|
||||
})
|
||||
|
||||
for i, row := range rows {
|
||||
expected := exp.expectedResponse.GetOk().GetRoutes()[0].Rows[i]
|
||||
if !proto.Equal(row, expected) {
|
||||
t.Fatalf("Expected: %+v\n Got: %+v", expected, row)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,6 +259,39 @@ func TestTopRoutes(t *testing.T) {
|
|||
testTopRoutes(t, expectations)
|
||||
})
|
||||
|
||||
t.Run("Successfully performs a routes query for a daemonset", func(t *testing.T) {
|
||||
routes := []string{"/a"}
|
||||
counts := []uint64{123}
|
||||
expectations := []topRoutesExpected{
|
||||
topRoutesExpected{
|
||||
expectedStatRPC: expectedStatRPC{
|
||||
err: nil,
|
||||
mockPromResponse: routesMetric([]string{"/a"}),
|
||||
expectedPrometheusQueries: []string{
|
||||
`histogram_quantile(0.5, sum(irate(route_response_latency_ms_bucket{daemonset="books", direction="inbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (le, dst, rt_route))`,
|
||||
`histogram_quantile(0.95, sum(irate(route_response_latency_ms_bucket{daemonset="books", direction="inbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (le, dst, rt_route))`,
|
||||
`histogram_quantile(0.99, sum(irate(route_response_latency_ms_bucket{daemonset="books", direction="inbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (le, dst, rt_route))`,
|
||||
`sum(increase(route_response_total{daemonset="books", direction="inbound", dst=~"(books.default.svc.cluster.local)(:\\d+)?", namespace="default"}[1m])) by (rt_route, dst, classification)`,
|
||||
},
|
||||
k8sConfigs: booksDSConfig,
|
||||
},
|
||||
req: pb.TopRoutesRequest{
|
||||
Selector: &pb.ResourceSelection{
|
||||
Resource: &pb.Resource{
|
||||
Namespace: "default",
|
||||
Type: pkgK8s.DaemonSet,
|
||||
Name: "books",
|
||||
},
|
||||
},
|
||||
TimeWindow: "1m",
|
||||
},
|
||||
expectedResponse: GenTopRoutesResponse(routes, counts, false, "books"),
|
||||
},
|
||||
}
|
||||
|
||||
testTopRoutes(t, expectations)
|
||||
})
|
||||
|
||||
t.Run("Successfully performs an outbound routes query", func(t *testing.T) {
|
||||
routes := []string{"/a"}
|
||||
counts := []uint64{123}
|
||||
|
|
|
@ -28,6 +28,7 @@ var (
|
|||
// destination resource on an outbound 'from' query
|
||||
ValidTargets = []string{
|
||||
k8s.Authority,
|
||||
k8s.DaemonSet,
|
||||
k8s.Deployment,
|
||||
k8s.Namespace,
|
||||
k8s.Pod,
|
||||
|
@ -37,6 +38,7 @@ var (
|
|||
// ValidTapDestinations specifies resource types allowed as a tap destination:
|
||||
// destination resource on an outbound 'to' query
|
||||
ValidTapDestinations = []string{
|
||||
k8s.DaemonSet,
|
||||
k8s.Deployment,
|
||||
k8s.Job,
|
||||
k8s.Namespace,
|
||||
|
|
|
@ -45,7 +45,7 @@ func main() {
|
|||
|
||||
var spClient *spclient.Clientset
|
||||
restrictToNamespace := ""
|
||||
resources := []k8s.APIResource{k8s.Deploy, k8s.Pod, k8s.RC, k8s.RS, k8s.Svc}
|
||||
resources := []k8s.APIResource{k8s.DS, k8s.Deploy, k8s.Pod, k8s.RC, k8s.RS, k8s.Svc}
|
||||
|
||||
if *singleNamespace {
|
||||
restrictToNamespace = *controllerNamespace
|
||||
|
|
|
@ -37,6 +37,7 @@ func main() {
|
|||
k8sClient,
|
||||
nil,
|
||||
restrictToNamespace,
|
||||
k8s.DS,
|
||||
k8s.Deploy,
|
||||
k8s.Pod,
|
||||
k8s.RC,
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -20,7 +21,8 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/informers"
|
||||
arinformers "k8s.io/client-go/informers/admissionregistration/v1beta1"
|
||||
appinformers "k8s.io/client-go/informers/apps/v1beta2"
|
||||
appv1informers "k8s.io/client-go/informers/apps/v1"
|
||||
appv1beta2informers "k8s.io/client-go/informers/apps/v1beta2"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
@ -34,6 +36,7 @@ type APIResource int
|
|||
const (
|
||||
CM APIResource = iota
|
||||
Deploy
|
||||
DS
|
||||
Endpoint
|
||||
MWC // mutating webhook configuration
|
||||
Pod
|
||||
|
@ -48,12 +51,13 @@ type API struct {
|
|||
Client kubernetes.Interface
|
||||
|
||||
cm coreinformers.ConfigMapInformer
|
||||
deploy appinformers.DeploymentInformer
|
||||
deploy appv1beta2informers.DeploymentInformer
|
||||
ds appv1informers.DaemonSetInformer
|
||||
endpoint coreinformers.EndpointsInformer
|
||||
mwc arinformers.MutatingWebhookConfigurationInformer
|
||||
pod coreinformers.PodInformer
|
||||
rc coreinformers.ReplicationControllerInformer
|
||||
rs appinformers.ReplicaSetInformer
|
||||
rs appv1beta2informers.ReplicaSetInformer
|
||||
sp spinformers.ServiceProfileInformer
|
||||
svc coreinformers.ServiceInformer
|
||||
|
||||
|
@ -101,6 +105,9 @@ func NewAPI(k8sClient kubernetes.Interface, spClient spclient.Interface, namespa
|
|||
case Deploy:
|
||||
api.deploy = sharedInformers.Apps().V1beta2().Deployments()
|
||||
api.syncChecks = append(api.syncChecks, api.deploy.Informer().HasSynced)
|
||||
case DS:
|
||||
api.ds = sharedInformers.Apps().V1().DaemonSets()
|
||||
api.syncChecks = append(api.syncChecks, api.ds.Informer().HasSynced)
|
||||
case Endpoint:
|
||||
api.endpoint = sharedInformers.Core().V1().Endpoints()
|
||||
api.syncChecks = append(api.syncChecks, api.endpoint.Informer().HasSynced)
|
||||
|
@ -144,15 +151,23 @@ func (api *API) Sync() {
|
|||
}
|
||||
|
||||
// Deploy provides access to a shared informer and lister for Deployments.
|
||||
func (api *API) Deploy() appinformers.DeploymentInformer {
|
||||
func (api *API) Deploy() appv1beta2informers.DeploymentInformer {
|
||||
if api.deploy == nil {
|
||||
panic("Deploy informer not configured")
|
||||
}
|
||||
return api.deploy
|
||||
}
|
||||
|
||||
// DS provides access to a shared informer and lister for Daemonsets.
|
||||
func (api *API) DS() appv1informers.DaemonSetInformer {
|
||||
if api.ds == nil {
|
||||
panic("DS informer not configured")
|
||||
}
|
||||
return api.ds
|
||||
}
|
||||
|
||||
// RS provides access to a shared informer and lister for ReplicaSets.
|
||||
func (api *API) RS() appinformers.ReplicaSetInformer {
|
||||
func (api *API) RS() appv1beta2informers.ReplicaSetInformer {
|
||||
if api.rs == nil {
|
||||
panic("RS informer not configured")
|
||||
}
|
||||
|
@ -223,6 +238,8 @@ func (api *API) GetObjects(namespace, restype, name string) ([]runtime.Object, e
|
|||
switch restype {
|
||||
case k8s.Namespace:
|
||||
return api.getNamespaces(name)
|
||||
case k8s.DaemonSet:
|
||||
return api.getDaemonsets(namespace, name)
|
||||
case k8s.Deployment:
|
||||
return api.getDeployments(namespace, name)
|
||||
case k8s.Pod:
|
||||
|
@ -271,6 +288,10 @@ func (api *API) GetPodsFor(obj runtime.Object, includeFailed bool) ([]*apiv1.Pod
|
|||
namespace = typed.Name
|
||||
selector = labels.Everything()
|
||||
|
||||
case *appsv1.DaemonSet:
|
||||
namespace = typed.Namespace
|
||||
selector = labels.Set(typed.Spec.Selector.MatchLabels).AsSelector()
|
||||
|
||||
case *appsv1beta2.Deployment:
|
||||
namespace = typed.Namespace
|
||||
selector = labels.Set(typed.Spec.Selector.MatchLabels).AsSelector()
|
||||
|
@ -326,6 +347,9 @@ func GetNameAndNamespaceOf(obj runtime.Object) (string, string, error) {
|
|||
case *apiv1.Namespace:
|
||||
return typed.Name, typed.Name, nil
|
||||
|
||||
case *appsv1.DaemonSet:
|
||||
return typed.Name, typed.Namespace, nil
|
||||
|
||||
case *appsv1beta2.Deployment:
|
||||
return typed.Name, typed.Namespace, nil
|
||||
|
||||
|
@ -481,6 +505,32 @@ func (api *API) getRCs(namespace, name string) ([]runtime.Object, error) {
|
|||
return objects, nil
|
||||
}
|
||||
|
||||
func (api *API) getDaemonsets(namespace, name string) ([]runtime.Object, error) {
|
||||
var err error
|
||||
var daemonsets []*appsv1.DaemonSet
|
||||
|
||||
if namespace == "" {
|
||||
daemonsets, err = api.DS().Lister().List(labels.Everything())
|
||||
} else if name == "" {
|
||||
daemonsets, err = api.DS().Lister().DaemonSets(namespace).List(labels.Everything())
|
||||
} else {
|
||||
var ds *appsv1.DaemonSet
|
||||
ds, err = api.DS().Lister().DaemonSets(namespace).Get(name)
|
||||
daemonsets = []*appsv1.DaemonSet{ds}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objects := []runtime.Object{}
|
||||
for _, ds := range daemonsets {
|
||||
objects = append(objects, ds)
|
||||
}
|
||||
|
||||
return objects, nil
|
||||
}
|
||||
|
||||
func (api *API) getServices(namespace, name string) ([]runtime.Object, error) {
|
||||
services, err := api.GetServices(namespace, name)
|
||||
|
||||
|
|
|
@ -129,6 +129,39 @@ apiVersion: apps/v1beta2
|
|||
kind: Deployment
|
||||
metadata:
|
||||
name: my-deploy
|
||||
namespace: not-my-ns`,
|
||||
},
|
||||
},
|
||||
getObjectsExpected{
|
||||
err: nil,
|
||||
namespace: "",
|
||||
resType: k8s.DaemonSet,
|
||||
name: "",
|
||||
k8sResResults: []string{`
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: my-deploy
|
||||
namespace: my-ns`,
|
||||
},
|
||||
},
|
||||
getObjectsExpected{
|
||||
err: nil,
|
||||
namespace: "my-ns",
|
||||
resType: k8s.DaemonSet,
|
||||
name: "my-ds",
|
||||
k8sResResults: []string{`
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: my-ds
|
||||
namespace: my-ns`,
|
||||
},
|
||||
k8sResMisc: []string{`
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: my-ds
|
||||
namespace: not-my-ns`,
|
||||
},
|
||||
},
|
||||
|
@ -362,6 +395,31 @@ status:
|
|||
getPodsForExpected{
|
||||
err: nil,
|
||||
k8sResInput: `
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: emoji
|
||||
namespace: emojivoto
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: emoji-svc`,
|
||||
k8sResResults: []string{`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: emojivoto-meshed
|
||||
namespace: emojivoto
|
||||
labels:
|
||||
app: emoji-svc
|
||||
status:
|
||||
phase: Running`,
|
||||
},
|
||||
k8sResMisc: []string{},
|
||||
},
|
||||
getPodsForExpected{
|
||||
err: nil,
|
||||
k8sResInput: `
|
||||
apiVersion: apps/v1beta2
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
|
|
|
@ -42,6 +42,7 @@ func NewFakeAPI(namespace string, configs ...string) (*API, error) {
|
|||
namespace,
|
||||
CM,
|
||||
Deploy,
|
||||
DS,
|
||||
Endpoint,
|
||||
Pod,
|
||||
RC,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -45,6 +45,7 @@ var AllResources = []string{
|
|||
// StatAllResourceTypes represents the resources to query in StatSummary when Resource.Type is "all"
|
||||
var StatAllResourceTypes = []string{
|
||||
// TODO: add Namespace here to decrease queries from the web process
|
||||
DaemonSet,
|
||||
Deployment,
|
||||
ReplicationController,
|
||||
Pod,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
## compile proxy-init utility
|
||||
FROM gcr.io/linkerd-io/go-deps:8c5ab859 as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:02bd701f as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY ./proxy-init ./proxy-init
|
||||
RUN CGO_ENABLED=0 GOOS=linux go install -v ./proxy-init/
|
||||
|
|
|
@ -26,7 +26,7 @@ COPY web/app .
|
|||
RUN $ROOT/bin/web build
|
||||
|
||||
## compile go server
|
||||
FROM gcr.io/linkerd-io/go-deps:8c5ab859 as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:02bd701f as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
RUN mkdir -p web
|
||||
COPY web/main.go web
|
||||
|
|
|
@ -142,6 +142,7 @@ class Namespaces extends React.Component {
|
|||
<NetworkGraph namespace={this.state.ns} deployments={metrics.deployment} />
|
||||
}
|
||||
{this.renderResourceSection("deployment", metrics.deployment)}
|
||||
{this.renderResourceSection("daemonset", metrics.daemonset)}
|
||||
{this.renderResourceSection("replicationcontroller", metrics.replicationcontroller)}
|
||||
{this.renderResourceSection("pod", metrics.pod)}
|
||||
{this.renderResourceSection("authority", metrics.authority)}
|
||||
|
|
|
@ -155,6 +155,7 @@ class NamespaceLanding extends React.Component {
|
|||
<Grid item>{ noMetrics ? <div>No resources detected.</div> : null}</Grid>
|
||||
|
||||
{this.renderResourceSection("deployment", metrics.deployment)}
|
||||
{this.renderResourceSection("daemonset", metrics.daemonset)}
|
||||
{this.renderResourceSection("replicationcontroller", metrics.replicationcontroller)}
|
||||
{this.renderResourceSection("pod", metrics.pod)}
|
||||
{this.renderResourceSection("authority", metrics.authority)}
|
||||
|
|
|
@ -69,6 +69,7 @@ class NavigationResourcesBase extends React.Component {
|
|||
<MenuList dense component="div" disablePadding>
|
||||
<NavigationResource type="authorities" />
|
||||
<NavigationResource type="deployments" metrics={allMetrics.deployment} />
|
||||
<NavigationResource type="daemonsets" metrics={allMetrics.daemonset} />
|
||||
<NavigationResource type="namespaces" metrics={nsMetrics.namespace} />
|
||||
<NavigationResource type="pods" metrics={allMetrics.pod} />
|
||||
<NavigationResource type="replicationcontrollers" metrics={allMetrics.replicationcontroller} />
|
||||
|
|
|
@ -127,6 +127,8 @@ export const friendlyTitle = singularOrPluralResource => {
|
|||
let titleCase = _startCase(resource);
|
||||
if (resource === "replicationcontroller") {
|
||||
titleCase = _startCase("replication controller");
|
||||
} else if (resource === "daemonset") {
|
||||
titleCase = _startCase("daemon set");
|
||||
}
|
||||
let titles = { singular: titleCase };
|
||||
if (resource === "authority") {
|
||||
|
|
|
@ -62,6 +62,9 @@ let applicationHtml = (
|
|||
<Route
|
||||
path={`${pathPrefix}/namespaces/:namespace/pods/:pod`}
|
||||
render={props => <Navigation {...props} ChildComponent={ResourceDetail} />} />
|
||||
<Route
|
||||
path={`${pathPrefix}/namespaces/:namespace/daemonsets/:daemonset`}
|
||||
render={props => <Navigation {...props} ChildComponent={ResourceDetail} />} />
|
||||
<Route
|
||||
path={`${pathPrefix}/namespaces/:namespace/deployments/:deployment`}
|
||||
render={props => <Navigation {...props} ChildComponent={ResourceDetail} />} />
|
||||
|
@ -83,6 +86,9 @@ let applicationHtml = (
|
|||
<Route
|
||||
path={`${pathPrefix}/deployments`}
|
||||
render={props => <Navigation {...props} ChildComponent={ResourceList} resource="deployment" />} />
|
||||
<Route
|
||||
path={`${pathPrefix}/daemonsets`}
|
||||
render={props => <Navigation {...props} ChildComponent={ResourceList} resource="daemonset" />} />
|
||||
<Route
|
||||
path={`${pathPrefix}/replicationcontrollers`}
|
||||
render={props => <Navigation {...props} ChildComponent={ResourceList} resource="replicationcontroller" />} />
|
||||
|
|
|
@ -94,11 +94,13 @@ func NewServer(
|
|||
server.router.GET("/servicemesh", handler.handleIndex)
|
||||
server.router.GET("/namespaces", handler.handleIndex)
|
||||
server.router.GET("/namespaces/:namespace", handler.handleIndex)
|
||||
server.router.GET("/daemonsets", handler.handleIndex)
|
||||
server.router.GET("/deployments", handler.handleIndex)
|
||||
server.router.GET("/replicationcontrollers", handler.handleIndex)
|
||||
server.router.GET("/pods", handler.handleIndex)
|
||||
server.router.GET("/authorities", handler.handleIndex)
|
||||
server.router.GET("/namespaces/:namespace/pods/:pod", handler.handleIndex)
|
||||
server.router.GET("/namespaces/:namespace/daemonsets/:daemonset", handler.handleIndex)
|
||||
server.router.GET("/namespaces/:namespace/deployments/:deployment", handler.handleIndex)
|
||||
server.router.GET("/namespaces/:namespace/replicationcontrollers/:replicationcontroller", handler.handleIndex)
|
||||
server.router.GET("/tap", handler.handleIndex)
|
||||
|
|
Loading…
Reference in New Issue