mirror of https://github.com/linkerd/linkerd2.git
Implement get all installed extensions (#6391)
Introduce new table in the control plane view of the dashboard to display installed extensions. Signed-off-by: Sanni Michael <sannimichaelse@gmail.com>
This commit is contained in:
parent
8c9930b4ac
commit
fb0b1c898e
|
@ -34,6 +34,17 @@ const styles = {
|
|||
},
|
||||
};
|
||||
|
||||
const installedExtensionsColumn = [
|
||||
{
|
||||
title: <Trans>columnTitleName</Trans>,
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: <Trans>columnTitleNamespace</Trans>,
|
||||
dataIndex: 'namespace',
|
||||
},
|
||||
];
|
||||
|
||||
const serviceMeshDetailsColumns = [
|
||||
{
|
||||
title: <Trans>columnTitleName</Trans>,
|
||||
|
@ -72,6 +83,7 @@ class ServiceMesh extends React.Component {
|
|||
this.state = {
|
||||
pollingInterval: 2000,
|
||||
components: [],
|
||||
extensions: [],
|
||||
nsStatuses: [],
|
||||
pendingRequests: false,
|
||||
loaded: false,
|
||||
|
@ -81,6 +93,7 @@ class ServiceMesh extends React.Component {
|
|||
|
||||
componentDidMount() {
|
||||
this.startServerPolling();
|
||||
this.fetchAllInstalledExtensions();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
|
@ -109,6 +122,12 @@ class ServiceMesh extends React.Component {
|
|||
];
|
||||
}
|
||||
|
||||
getInstalledExtensions() {
|
||||
const { extensions } = this.state;
|
||||
const extensionList = !_isEmpty(extensions.extensions) ? extensions.extensions : [];
|
||||
return extensionList;
|
||||
}
|
||||
|
||||
getControllerComponentData = podData => {
|
||||
const podDataByDeploy = _groupBy(_filter(podData.pods, d => d.controlPlane), p => p.deployment);
|
||||
const byDeployName = _mapKeys(podDataByDeploy, (_pods, dep) => dep.split('/')[1]);
|
||||
|
@ -190,6 +209,15 @@ class ServiceMesh extends React.Component {
|
|||
.catch(this.handleApiError);
|
||||
}
|
||||
|
||||
fetchAllInstalledExtensions() {
|
||||
this.api.setCurrentRequests([this.api.fetchExtension()]);
|
||||
this.serverPromise = Promise.all(this.api.getCurrentPromises())
|
||||
.then(([extensions]) => {
|
||||
this.setState({ extensions });
|
||||
})
|
||||
.catch(this.handleApiError);
|
||||
}
|
||||
|
||||
handleApiError(e) {
|
||||
if (e.isCanceled) {
|
||||
return;
|
||||
|
@ -234,6 +262,23 @@ class ServiceMesh extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderInstalledExtensions() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid container justify="space-between">
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h6"><Trans>Installed Extensions</Trans></Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<BaseTable
|
||||
tableClassName="metric-table"
|
||||
tableRows={this.getInstalledExtensions()}
|
||||
tableColumns={installedExtensionsColumn}
|
||||
rowKey={d => d.uid} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderServiceMeshDetails() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
@ -298,6 +343,7 @@ class ServiceMesh extends React.Component {
|
|||
<Grid container spacing={3}>
|
||||
<Grid item xs={8} container direction="column">
|
||||
<Grid item>{this.renderControlPlaneDetails()}</Grid>
|
||||
<Grid item>{this.renderInstalledExtensions()}</Grid>
|
||||
<Grid item>
|
||||
<MeshedStatusTable tableRows={nsStatuses} />
|
||||
</Grid>
|
||||
|
|
|
@ -61,7 +61,7 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
|
|||
const servicesPath = '/api/services';
|
||||
const edgesPath = '/api/edges';
|
||||
const gatewaysPath = '/api/gateways';
|
||||
const l5dExtensionsPath = '/api/extension';
|
||||
const l5dExtensionsPath = '/api/extensions';
|
||||
|
||||
const validMetricsWindows = {
|
||||
'10s': '10 minutes',
|
||||
|
@ -147,7 +147,11 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
|
|||
};
|
||||
|
||||
const fetchExtension = name => {
|
||||
return apiFetch(`${l5dExtensionsPath}?extension_name=${name}`);
|
||||
let extensionPath = l5dExtensionsPath;
|
||||
if (name) {
|
||||
extensionPath += `?extension_name=${name}`;
|
||||
}
|
||||
return apiFetch(extensionPath);
|
||||
};
|
||||
|
||||
const fetchResourceDefinition = (namespace, resourceType, resourceName) => {
|
||||
|
|
|
@ -434,23 +434,54 @@ func (h *handler) handleAPIResourceDefinition(w http.ResponseWriter, req *http.R
|
|||
w.Write(resourceDefinition)
|
||||
}
|
||||
|
||||
func (h *handler) handleGetExtension(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
|
||||
func (h *handler) handleGetExtensions(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
|
||||
ctx := req.Context()
|
||||
extensionName := req.FormValue("extension_name")
|
||||
|
||||
type Extension struct {
|
||||
Name string `json:"name"`
|
||||
UID string `json:"uid"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
resp := map[string]interface{}{}
|
||||
ns, err := h.k8sAPI.GetNamespaceWithExtensionLabel(ctx, extensionName)
|
||||
if err != nil && strings.HasPrefix(err.Error(), "could not find") {
|
||||
if extensionName != "" {
|
||||
ns, err := h.k8sAPI.GetNamespaceWithExtensionLabel(ctx, extensionName)
|
||||
if err != nil && strings.HasPrefix(err.Error(), "could not find") {
|
||||
renderJSON(w, resp)
|
||||
return
|
||||
} else if err != nil {
|
||||
renderJSONError(w, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
resp["data"] = Extension{
|
||||
UID: string(ns.UID),
|
||||
Name: ns.GetLabels()[k8s.LinkerdExtensionLabel],
|
||||
Namespace: ns.Name,
|
||||
}
|
||||
|
||||
renderJSON(w, resp)
|
||||
return
|
||||
} else if err != nil {
|
||||
}
|
||||
|
||||
installedExtensions, err := h.k8sAPI.GetAllNamespacesWithExtensionLabel(ctx)
|
||||
if err != nil {
|
||||
renderJSONError(w, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
resp["extensionName"] = ns.GetLabels()[k8s.LinkerdExtensionLabel]
|
||||
resp["namespace"] = ns.Name
|
||||
extensionList := make([]Extension, len(installedExtensions))
|
||||
|
||||
for i, installedExtension := range installedExtensions {
|
||||
extensionList[i] = Extension{
|
||||
UID: string(installedExtension.GetObjectMeta().GetUID()),
|
||||
Name: installedExtension.GetLabels()[k8s.LinkerdExtensionLabel],
|
||||
Namespace: installedExtension.GetName(),
|
||||
}
|
||||
}
|
||||
|
||||
resp["extensions"] = extensionList
|
||||
renderJSON(w, resp)
|
||||
}
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ func NewServer(
|
|||
server.router.GET("/api/check", handler.handleAPICheck)
|
||||
server.router.GET("/api/resource-definition", handler.handleAPIResourceDefinition)
|
||||
server.router.GET("/api/gateways", handler.handleAPIGateways)
|
||||
server.router.GET("/api/extension", handler.handleGetExtension)
|
||||
server.router.GET("/api/extensions", handler.handleGetExtensions)
|
||||
|
||||
// grafana proxy
|
||||
server.handleAllOperationsForPath("/grafana/*grafanapath", handler.handleGrafana)
|
||||
|
|
Loading…
Reference in New Issue