Skip to content

Commit 12d4439

Browse files
authored
Merge pull request #217 from arangodb/feature/dashboard-other-operators
Add links to other operators in dashboard menu
2 parents 65cc238 + 4484172 commit 12d4439

File tree

14 files changed

+370
-99
lines changed

14 files changed

+370
-99
lines changed

dashboard/assets.go

+49-49
Large diffs are not rendered by default.

dashboard/src/App.js

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Container, Segment, Message } from 'semantic-ui-react';
1+
import { Container, Segment, Menu, Message } from 'semantic-ui-react';
22
import React, { Component } from 'react';
33
import ReactTimeout from 'react-timeout';
44

@@ -19,7 +19,14 @@ const PodInfoView = ({pod, namespace}) => (
1919
</Segment>
2020
);
2121

22-
const OperatorsView = ({error, deployment, deploymentReplication, storage, pod, namespace}) => {
22+
const OperatorsView = ({error, deployment, deploymentReplication, storage, pod, namespace, otherOperators}) => {
23+
let commonMenuItems = otherOperators.map((item) => <Menu.Item><a href={item.url}>{operatorType2Name(item.type)}</a></Menu.Item>);
24+
if (commonMenuItems.length > 0) {
25+
commonMenuItems = (<Menu.Item>
26+
<Menu.Header>Other operators</Menu.Header>
27+
<Menu.Menu>{commonMenuItems}</Menu.Menu>
28+
</Menu.Item>);
29+
}
2330
let Operator = NoOperator;
2431
if (deployment)
2532
Operator = DeploymentOperator;
@@ -30,11 +37,25 @@ const OperatorsView = ({error, deployment, deploymentReplication, storage, pod,
3037
return (
3138
<Operator
3239
podInfoView={<PodInfoView pod={pod} namespace={namespace} />}
40+
commonMenuItems={commonMenuItems}
3341
error={error}
3442
/>
3543
);
3644
}
3745

46+
const operatorType2Name = (oType) => {
47+
switch (oType) {
48+
case "deployment":
49+
return "Deployments";
50+
case "deployment_replication":
51+
return "Deployment replications";
52+
case "storage":
53+
return "Storage";
54+
default:
55+
return "";
56+
}
57+
};
58+
3859
const LoadingView = () => (
3960
<Container>
4061
<Loading/>
@@ -76,6 +97,7 @@ class App extends Component {
7697
deployment={this.state.operators.deployment}
7798
deploymentReplication={this.state.operators.deployment_replication}
7899
storage={this.state.operators.storage}
100+
otherOperators={this.state.operators.other || []}
79101
pod={this.state.operators.pod}
80102
namespace={this.state.operators.namespace}
81103
/>;

dashboard/src/deployment/DeploymentOperator.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,16 @@ class DeploymentOperator extends Component {
3434
{doLogout =>
3535
<StyledMenu fixed="left" vertical>
3636
<Menu.Item>
37-
<Link to="/">Deployments</Link>
38-
</Menu.Item>
39-
<Menu.Item position="right" onClick={() => doLogout()}>
40-
Logout
37+
<Menu.Header>Deployment Operator</Menu.Header>
38+
<Menu.Menu>
39+
<Menu.Item>
40+
<Link to="/">Deployments</Link>
41+
</Menu.Item>
42+
<Menu.Item position="right" onClick={() => doLogout()}>
43+
Logout
44+
</Menu.Item>
45+
</Menu.Menu>
46+
{this.props.commonMenuItems}
4147
</Menu.Item>
4248
</StyledMenu>
4349
}

dashboard/src/replication/DeploymentReplicationOperator.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,17 @@ class DeploymentReplicationOperator extends Component {
3434
{doLogout =>
3535
<StyledMenu fixed="left" vertical>
3636
<Menu.Item>
37-
<Link to="/">Deployment replications</Link>
38-
</Menu.Item>
39-
<Menu.Item position="right" onClick={() => doLogout()}>
40-
Logout
37+
<Menu.Header>Deployment Replication Operator</Menu.Header>
38+
<Menu.Menu>
39+
<Menu.Item>
40+
<Link to="/">Deployment replications</Link>
41+
</Menu.Item>
42+
<Menu.Item position="right" onClick={() => doLogout()}>
43+
Logout
44+
</Menu.Item>
45+
</Menu.Menu>
4146
</Menu.Item>
47+
{this.props.commonMenuItems}
4248
</StyledMenu>
4349
}
4450
</LogoutContext.Consumer>

dashboard/src/storage/StorageOperator.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,20 @@ class StorageOperator extends Component {
2020
<div>
2121
<LogoutContext.Consumer>
2222
{doLogout =>
23-
<StyledMenu fixed="left" vertical>
23+
<StyledMenu fixed="left" vertical>
2424
<Menu.Item>
25-
Local storages
25+
<Menu.Header>Deployment Operator</Menu.Header>
26+
<Menu.Menu>
27+
<Menu.Item>
28+
Local storages
29+
</Menu.Item>
30+
<Menu.Item position="right" onClick={() => doLogout()}>
31+
Logout
32+
</Menu.Item>
33+
</Menu.Menu>
34+
{this.props.commonMenuItems}
2635
</Menu.Item>
27-
<Menu.Item position="right" onClick={() => doLogout()}>
28-
Logout
29-
</Menu.Item>
30-
</StyledMenu>
36+
</StyledMenu>
3137
}
3238
</LogoutContext.Consumer>
3339
<StyledContentBox>

manifests/templates/deployment-replication/rbac.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ rules:
3030
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets"]
3131
verbs: ["*"]
3232
- apiGroups: [""]
33-
resources: ["nodes"]
34-
verbs: ["get"]
33+
resources: ["namespaces", "nodes"]
34+
verbs: ["get", "list"]
3535
- apiGroups: ["apps"]
3636
resources: ["deployments", "replicasets"]
3737
verbs: ["get"]

manifests/templates/deployment/rbac.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ rules:
2727
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets"]
2828
verbs: ["*"]
2929
- apiGroups: [""]
30-
resources: ["nodes"]
31-
verbs: ["get"]
30+
resources: ["namespaces", "nodes"]
31+
verbs: ["get", "list"]
3232
- apiGroups: ["apps"]
3333
resources: ["deployments", "replicasets"]
3434
verbs: ["get"]

manifests/templates/storage/rbac.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ rules:
3333
- apiGroups: [""]
3434
resources: ["secrets"]
3535
verbs: ["get"]
36+
- apiGroups: [""]
37+
resources: ["namespaces", "nodes"]
38+
verbs: ["get", "list"]
3639
- apiGroups: ["apps"]
3740
resources: ["daemonsets"]
3841
verbs: ["*"]

pkg/deployment/server_api.go

+15-21
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@
2323
package deployment
2424

2525
import (
26-
"fmt"
27-
"net"
2826
"sort"
29-
"strconv"
3027

3128
"k8s.io/api/core/v1"
3229
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -197,28 +194,25 @@ func (d *Deployment) DatabaseURL() string {
197194
if err != nil {
198195
return ""
199196
}
200-
host := ""
201-
switch svc.Spec.Type {
202-
case v1.ServiceTypeLoadBalancer:
203-
for _, i := range svc.Status.LoadBalancer.Ingress {
204-
if i.Hostname != "" {
205-
host = i.Hostname
206-
} else {
207-
host = i.IP
208-
}
209-
break
210-
}
211-
case v1.ServiceTypeNodePort:
212-
// TODO
213-
}
214-
if host == "" {
215-
return ""
216-
}
217197
scheme := "https"
218198
if !d.GetSpec().IsSecure() {
219199
scheme = "http"
220200
}
221-
return fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(host, strconv.Itoa(k8sutil.ArangoPort)))
201+
nodeFetcher := func() (v1.NodeList, error) {
202+
result, err := d.deps.KubeCli.CoreV1().Nodes().List(metav1.ListOptions{})
203+
if err != nil {
204+
return v1.NodeList{}, maskAny(err)
205+
}
206+
return *result, nil
207+
}
208+
portPredicate := func(p v1.ServicePort) bool {
209+
return p.TargetPort.IntValue() == k8sutil.ArangoPort
210+
}
211+
url, err := k8sutil.CreateServiceURL(*svc, scheme, portPredicate, nodeFetcher)
212+
if err != nil {
213+
return ""
214+
}
215+
return url
222216
}
223217

224218
// DatabaseVersion returns the version used by the deployment

pkg/logging/logger.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ var (
3636
// The defaultLevels list is used during development to increase the
3737
// default level for components that we care a little less about.
3838
defaultLevels = map[string]string{
39-
"operator": "info",
40-
//"something.status": "info",
39+
//"operator": "info",
40+
//"something.status": "info",
4141
}
4242
)
4343

pkg/operator/server_discovery_api.go

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package operator
24+
25+
import (
26+
"sort"
27+
28+
"github.com/rs/zerolog"
29+
"k8s.io/api/core/v1"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
32+
"github.com/arangodb/kube-arangodb/pkg/server"
33+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
34+
)
35+
36+
const (
37+
appKey = "app"
38+
roleKey = "role"
39+
appDeploymentOperator = "arango-deployment-operator"
40+
appDeploymentReplicationOperator = "arango-deployment-replication-operator"
41+
appStorageOperator = "arango-storage-operator"
42+
roleLeader = "leader"
43+
)
44+
45+
// FindOtherOperators looks up references to other operators in the same Kubernetes cluster.
46+
func (o *Operator) FindOtherOperators() []server.OperatorReference {
47+
log := o.log
48+
var result []server.OperatorReference
49+
namespaces, err := o.Dependencies.KubeCli.CoreV1().Namespaces().List(metav1.ListOptions{})
50+
if err != nil {
51+
log.Warn().Err(err).Msg("Failed to list namespaces")
52+
} else {
53+
for _, ns := range namespaces.Items {
54+
if ns.Name != o.Config.Namespace {
55+
log.Debug().Str("namespace", ns.Name).Msg("inspecting namespace for operators")
56+
refs := o.findOtherOperatorsInNamespace(log, ns.Name, func(server.OperatorType) bool { return true })
57+
result = append(result, refs...)
58+
} else {
59+
log.Debug().Str("namespace", ns.Name).Msg("skip inspecting my own namespace for operators")
60+
}
61+
}
62+
}
63+
refs := o.findOtherOperatorsInNamespace(log, o.Config.Namespace, func(oType server.OperatorType) bool {
64+
// Exclude those operators that I provide myself.
65+
switch oType {
66+
case server.OperatorTypeDeployment:
67+
return !o.Dependencies.DeploymentProbe.IsReady()
68+
case server.OperatorTypeDeploymentReplication:
69+
return !o.Dependencies.DeploymentReplicationProbe.IsReady()
70+
case server.OperatorTypeStorage:
71+
return !o.Dependencies.StorageProbe.IsReady()
72+
default:
73+
return true
74+
}
75+
})
76+
result = append(result, refs...)
77+
sort.Slice(result, func(i, j int) bool {
78+
if result[i].Namespace == result[j].Namespace {
79+
return result[i].Type < result[j].Type
80+
}
81+
return result[i].Namespace < result[j].Namespace
82+
})
83+
84+
return result
85+
}
86+
87+
// findOtherOperatorsInNamespace looks up references to other operators in the given namespace.
88+
func (o *Operator) findOtherOperatorsInNamespace(log zerolog.Logger, namespace string, typePred func(server.OperatorType) bool) []server.OperatorReference {
89+
log = log.With().Str("namespace", namespace).Logger()
90+
var result []server.OperatorReference
91+
services, err := o.Dependencies.KubeCli.CoreV1().Services(namespace).List(metav1.ListOptions{})
92+
if err != nil {
93+
log.Debug().Err(err).Msg("Failed to list services")
94+
return nil
95+
}
96+
nodeFetcher := func() (v1.NodeList, error) {
97+
result, err := o.Dependencies.KubeCli.CoreV1().Nodes().List(metav1.ListOptions{})
98+
if err != nil {
99+
return v1.NodeList{}, maskAny(err)
100+
}
101+
return *result, nil
102+
}
103+
for _, svc := range services.Items {
104+
// Filter out unwanted services
105+
selector := svc.Spec.Selector
106+
if selector[roleKey] != roleLeader {
107+
log.Debug().Str("service", svc.Name).Msg("Service has no leader role selector")
108+
continue
109+
}
110+
var oType server.OperatorType
111+
switch selector[appKey] {
112+
case appDeploymentOperator:
113+
oType = server.OperatorTypeDeployment
114+
case appDeploymentReplicationOperator:
115+
oType = server.OperatorTypeDeploymentReplication
116+
case appStorageOperator:
117+
oType = server.OperatorTypeStorage
118+
default:
119+
log.Debug().Str("service", svc.Name).Msg("Service has no or invalid app selector")
120+
continue
121+
}
122+
if !typePred(oType) {
123+
continue
124+
}
125+
var url string
126+
switch svc.Spec.Type {
127+
case v1.ServiceTypeNodePort, v1.ServiceTypeLoadBalancer:
128+
if x, err := k8sutil.CreateServiceURL(svc, "https", nil, nodeFetcher); err == nil {
129+
url = x
130+
} else {
131+
log.Warn().Err(err).Str("service", svc.Name).Msg("Failed to create URL for service")
132+
}
133+
default:
134+
// No suitable service type
135+
continue
136+
}
137+
result = append(result, server.OperatorReference{
138+
Namespace: svc.GetNamespace(),
139+
URL: url,
140+
Type: oType,
141+
})
142+
}
143+
144+
log.Debug().Msgf("Found %d operator services", len(result))
145+
return result
146+
}

0 commit comments

Comments
 (0)