mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
Extensing deployment list
This commit is contained in:
parent
7c7849ac46
commit
81b98028b3
10 changed files with 357 additions and 77 deletions
File diff suppressed because one or more lines are too long
67
dashboard/package-lock.json
generated
67
dashboard/package-lock.json
generated
|
@ -4,6 +4,22 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.0.0-beta.51",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.51.tgz",
|
||||
"integrity": "sha1-SLjtGDBwNMZiD2Q1FGUMoszAFlo=",
|
||||
"requires": {
|
||||
"core-js": "^2.5.7",
|
||||
"regenerator-runtime": "^0.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
|
||||
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||
|
@ -1334,6 +1350,7 @@
|
|||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
|
@ -1342,7 +1359,8 @@
|
|||
"core-js": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
|
||||
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
|
||||
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2396,6 +2414,14 @@
|
|||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
|
||||
"dev": true
|
||||
},
|
||||
"copy-to-clipboard": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz",
|
||||
"integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==",
|
||||
"requires": {
|
||||
"toggle-selection": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||
|
@ -7037,6 +7063,11 @@
|
|||
"integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=",
|
||||
"dev": true
|
||||
},
|
||||
"keyboard-key": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/keyboard-key/-/keyboard-key-1.0.1.tgz",
|
||||
"integrity": "sha512-OAfjaSI917BOonwfH6LQHMZJRv5035jjZvgElouB/DM4I7l5zEjrA15RD80YwIjhN69xqEfWCZIbhBcGpb85Ig=="
|
||||
},
|
||||
"killable": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz",
|
||||
|
@ -9847,6 +9878,15 @@
|
|||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"react-copy-to-clipboard": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz",
|
||||
"integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==",
|
||||
"requires": {
|
||||
"copy-to-clipboard": "^3",
|
||||
"prop-types": "^15.5.8"
|
||||
}
|
||||
},
|
||||
"react-dev-utils": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz",
|
||||
|
@ -10532,15 +10572,16 @@
|
|||
}
|
||||
},
|
||||
"semantic-ui-react": {
|
||||
"version": "0.78.3",
|
||||
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.78.3.tgz",
|
||||
"integrity": "sha512-JRmuqjyigCehHfzrS2ir5nGoytZWCifU8G2T++G/CMdahUJBME7S6E9rU7WW9Qg2Fqn2aJIxfn6Ry/rlOTJDOw==",
|
||||
"version": "0.81.3",
|
||||
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.81.3.tgz",
|
||||
"integrity": "sha512-AGLKjtWT0HnOyduMJn+6T4JmV5DfEuOfN2iSpBmQ5ZNJxrmdD4hoiskP89MUT7JD4Mno7aH8KkzdIKxavxpJUw==",
|
||||
"requires": {
|
||||
"babel-runtime": "^6.25.0",
|
||||
"@babel/runtime": "^7.0.0-beta.49",
|
||||
"classnames": "^2.2.5",
|
||||
"fbjs": "^0.8.16",
|
||||
"lodash": "^4.17.4",
|
||||
"prop-types": "^15.5.10"
|
||||
"keyboard-key": "^1.0.1",
|
||||
"lodash": "^4.17.10",
|
||||
"prop-types": "^15.6.1",
|
||||
"shallowequal": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
@ -10676,6 +10717,11 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"shallowequal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
|
@ -11446,6 +11492,11 @@
|
|||
"repeat-string": "^1.6.1"
|
||||
}
|
||||
},
|
||||
"toggle-selection": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
|
||||
},
|
||||
"toposort": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"react": "^16.4.1",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"semantic-ui-less": "^2.2.12",
|
||||
"semantic-ui-react": "^0.78.2"
|
||||
"semantic-ui-react": "^0.81.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-scripts": "1.1.4"
|
||||
},
|
||||
"proxy": "https://kube-arangodb-operator:8528",
|
||||
"proxy": "https://192.168.140.208:8528",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<!-- meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" -->
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, { Component } from 'react';
|
||||
import { apiGet } from '../api/api.js';
|
||||
import { Icon, Table } from 'semantic-ui-react';
|
||||
import { Icon, Popup, Table } from 'semantic-ui-react';
|
||||
import Loading from '../util/Loading.js';
|
||||
import CommandInstruction from '../util/CommandInstruction.js';
|
||||
|
||||
const HeaderView = () => (
|
||||
<Table.Header>
|
||||
|
@ -9,17 +10,47 @@ const HeaderView = () => (
|
|||
<Table.HeaderCell>State</Table.HeaderCell>
|
||||
<Table.HeaderCell>Name</Table.HeaderCell>
|
||||
<Table.HeaderCell>Mode</Table.HeaderCell>
|
||||
<Table.HeaderCell>Pods</Table.HeaderCell>
|
||||
<Table.HeaderCell>Version</Table.HeaderCell>
|
||||
<Table.HeaderCell><Popup trigger={<span>Pods</span>}>Ready / Total</Popup></Table.HeaderCell>
|
||||
<Table.HeaderCell><Popup trigger={<span>Volumes</span>}>Bound / Total</Popup></Table.HeaderCell>
|
||||
<Table.HeaderCell>StorageClass</Table.HeaderCell>
|
||||
<Table.HeaderCell></Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
);
|
||||
|
||||
const RowView = ({name, mode, ready_pod_count, pod_count}) => (
|
||||
const DatabaseLinkView = ({name, url}) => (
|
||||
<a href={url} target={name}>
|
||||
<Popup trigger={<Icon link name="database"/>}>
|
||||
Go the the web-UI of the database.
|
||||
</Popup>
|
||||
</a>
|
||||
);
|
||||
|
||||
const NoDatabaseLinkView = () => (
|
||||
<Popup trigger={<Icon link name="database"/>}>
|
||||
This deployment is not reachable outside the Kubernetes cluster.
|
||||
</Popup>
|
||||
);
|
||||
|
||||
const RowView = ({name, mode, version, license, ready_pod_count, pod_count, ready_volume_count, volume_count, storage_classes, database_url, delete_command}) => (
|
||||
<Table.Row>
|
||||
<Table.Cell><Icon name="bell" color="red"/></Table.Cell>
|
||||
<Table.Cell>{name}</Table.Cell>
|
||||
<Table.Cell>{mode}</Table.Cell>
|
||||
<Table.Cell>{version} {(license) ? `(${license})` : "" }</Table.Cell>
|
||||
<Table.Cell>{ready_pod_count} / {pod_count}</Table.Cell>
|
||||
<Table.Cell>{ready_volume_count} / {volume_count}</Table.Cell>
|
||||
<Table.Cell>{storage_classes.map((item) => (item === "") ? "<default>" : item)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{ database_url ? <DatabaseLinkView name={name} url={database_url}/> : <NoDatabaseLinkView/>}
|
||||
<CommandInstruction
|
||||
trigger={<Icon floated="right" name="trash alternate"/>}
|
||||
command={delete_command}
|
||||
title="Delete deployment"
|
||||
description="To delete this deployment, run:"
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
|
||||
|
@ -32,9 +63,17 @@ const ListView = ({items}) => (
|
|||
<RowView
|
||||
key={item.name}
|
||||
name={item.name}
|
||||
namespace={item.namespace}
|
||||
mode={item.mode}
|
||||
version={item.database_version}
|
||||
license={item.database_license}
|
||||
ready_pod_count={item.ready_pod_count}
|
||||
pod_count={item.pod_count}
|
||||
ready_volume_count={item.ready_volume_count}
|
||||
volume_count={item.volume_count}
|
||||
storage_classes={item.storage_classes}
|
||||
database_url={item.database_url}
|
||||
delete_command={createDeleteCommand(item.name, item.namespace)}
|
||||
/>) : <p>No items</p>
|
||||
}
|
||||
</Table.Body>
|
||||
|
@ -43,6 +82,10 @@ const ListView = ({items}) => (
|
|||
|
||||
const EmptyView = () => (<div>No deployments</div>);
|
||||
|
||||
function createDeleteCommand(name, namespace) {
|
||||
return `kubectl delete ArangoDeployment -n ${namespace} ${name}`;
|
||||
}
|
||||
|
||||
class DeploymentList extends Component {
|
||||
state = {};
|
||||
|
||||
|
|
40
dashboard/src/util/CommandInstruction.js
Normal file
40
dashboard/src/util/CommandInstruction.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Button, Modal, Segment } from 'semantic-ui-react';
|
||||
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
||||
|
||||
class CommandInstruction extends Component {
|
||||
state = {};
|
||||
|
||||
close = () => { this.setState({open:false}); }
|
||||
open = () => { this.setState({open:true}); }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal trigger={this.props.trigger} onClose={this.close} onOpen={this.open} open={this.state.open}>
|
||||
<Modal.Header>{this.props.title}</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Modal.Description>
|
||||
<p>
|
||||
{this.props.description}
|
||||
</p>
|
||||
<Segment clearing>
|
||||
<code>{this.props.command}</code>
|
||||
</Segment>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<CopyToClipboard text={this.props.command} onCopy={this.close}>
|
||||
<Button
|
||||
positive
|
||||
icon='copy'
|
||||
labelPosition='right'
|
||||
content="Copy"
|
||||
/>
|
||||
</CopyToClipboard>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CommandInstruction;
|
|
@ -6,7 +6,7 @@ const Loading = ({message}) => (
|
|||
<Dimmer inverted active>
|
||||
<Loader inverted>{message || "Loading..."}</Loader>
|
||||
</Dimmer>
|
||||
<div style={{"min-height":"3em"}}/>
|
||||
<div style={{minHeight:"3em"}}/>
|
||||
</Segment>
|
||||
);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ rules:
|
|||
verbs: ["*"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get"]
|
||||
verbs: ["get", "update"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["daemonsets"]
|
||||
verbs: ["*"]
|
||||
|
|
|
@ -23,7 +23,16 @@
|
|||
package deployment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
// Name returns the name of the deployment.
|
||||
|
@ -31,6 +40,11 @@ func (d *Deployment) Name() string {
|
|||
return d.apiObject.Name
|
||||
}
|
||||
|
||||
// Namespace returns the namespace that contains the deployment.
|
||||
func (d *Deployment) Namespace() string {
|
||||
return d.apiObject.Namespace
|
||||
}
|
||||
|
||||
// Mode returns the mode of the deployment.
|
||||
func (d *Deployment) Mode() api.DeploymentMode {
|
||||
return d.GetSpec().GetMode()
|
||||
|
@ -68,3 +82,113 @@ func (d *Deployment) ReadyPodCount() int {
|
|||
})
|
||||
return count
|
||||
}
|
||||
|
||||
// VolumeCount returns the number of volumes for the deployment
|
||||
func (d *Deployment) VolumeCount() int {
|
||||
count := 0
|
||||
status, _ := d.GetStatus()
|
||||
status.Members.ForeachServerGroup(func(group api.ServerGroup, list api.MemberStatusList) error {
|
||||
for _, m := range list {
|
||||
if m.PersistentVolumeClaimName != "" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return count
|
||||
}
|
||||
|
||||
// ReadyVolumeCount returns the number of volumes for the deployment that are in ready state
|
||||
func (d *Deployment) ReadyVolumeCount() int {
|
||||
count := 0
|
||||
status, _ := d.GetStatus()
|
||||
pvcs, _ := d.GetOwnedPVCs() // Ignore errors on purpose
|
||||
status.Members.ForeachServerGroup(func(group api.ServerGroup, list api.MemberStatusList) error {
|
||||
for _, m := range list {
|
||||
if m.PersistentVolumeClaimName == "" {
|
||||
continue
|
||||
}
|
||||
// Find status
|
||||
for _, pvc := range pvcs {
|
||||
if pvc.Name == m.PersistentVolumeClaimName {
|
||||
if pvc.Status.Phase == v1.ClaimBound {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return count
|
||||
}
|
||||
|
||||
// StorageClasses returns the names of the StorageClasses used by this deployment.
|
||||
func (d *Deployment) StorageClasses() []string {
|
||||
scNames := make(map[string]struct{})
|
||||
spec := d.GetSpec()
|
||||
mode := spec.GetMode()
|
||||
if mode.HasAgents() {
|
||||
scNames[spec.Agents.GetStorageClassName()] = struct{}{}
|
||||
}
|
||||
if mode.HasDBServers() {
|
||||
scNames[spec.DBServers.GetStorageClassName()] = struct{}{}
|
||||
}
|
||||
if mode.HasSingleServers() {
|
||||
scNames[spec.Single.GetStorageClassName()] = struct{}{}
|
||||
}
|
||||
result := make([]string, 0, len(scNames))
|
||||
for k := range scNames {
|
||||
result = append(result, k)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// DatabaseURL returns an URL to reach the database from outside the Kubernetes cluster
|
||||
// Empty string means that the database is not reachable outside the Kubernetes cluster.
|
||||
func (d *Deployment) DatabaseURL() string {
|
||||
eaSvcName := k8sutil.CreateDatabaseExternalAccessServiceName(d.Name())
|
||||
ns := d.apiObject.Namespace
|
||||
svc, err := d.deps.KubeCli.CoreV1().Services(ns).Get(eaSvcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
host := ""
|
||||
switch svc.Spec.Type {
|
||||
case v1.ServiceTypeLoadBalancer:
|
||||
for _, i := range svc.Status.LoadBalancer.Ingress {
|
||||
if i.Hostname != "" {
|
||||
host = i.Hostname
|
||||
} else {
|
||||
host = i.IP
|
||||
}
|
||||
break
|
||||
}
|
||||
case v1.ServiceTypeNodePort:
|
||||
// TODO
|
||||
}
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
scheme := "https"
|
||||
if !d.GetSpec().IsSecure() {
|
||||
scheme = "http"
|
||||
}
|
||||
return fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(host, strconv.Itoa(k8sutil.ArangoPort)))
|
||||
}
|
||||
|
||||
// DatabaseVersion returns the version used by the deployment
|
||||
// Returns versionNumber, licenseType
|
||||
func (d *Deployment) DatabaseVersion() (string, string) {
|
||||
image := d.GetSpec().GetImage()
|
||||
status, _ := d.GetStatus()
|
||||
info, found := status.Images.GetByImage(image)
|
||||
if !found {
|
||||
return "", ""
|
||||
}
|
||||
license := "community"
|
||||
if info.Enterprise {
|
||||
license = "enterprise"
|
||||
}
|
||||
return string(info.ArangoDBVersion), license
|
||||
}
|
||||
|
|
|
@ -33,9 +33,15 @@ import (
|
|||
// Deployment is the API implemented by an ArangoDeployment.
|
||||
type Deployment interface {
|
||||
Name() string
|
||||
Namespace() string
|
||||
Mode() api.DeploymentMode
|
||||
PodCount() int
|
||||
ReadyPodCount() int
|
||||
VolumeCount() int
|
||||
ReadyVolumeCount() int
|
||||
StorageClasses() []string
|
||||
DatabaseURL() string
|
||||
DatabaseVersion() (string, string)
|
||||
}
|
||||
|
||||
// DeploymentOperator is the API implemented by the deployment operator.
|
||||
|
@ -46,9 +52,16 @@ type DeploymentOperator interface {
|
|||
// DeploymentInfo is the information returned per deployment.
|
||||
type DeploymentInfo struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Mode api.DeploymentMode `json:"mode"`
|
||||
PodCount int `json:"pod_count"`
|
||||
ReadyPodCount int `json:"ready_pod_count"`
|
||||
VolumeCount int `json:"volume_count"`
|
||||
ReadyVolumeCount int `json:"ready_volume_count"`
|
||||
StorageClasses []string `json:"storage_classes"`
|
||||
DatabaseURL string `json:"database_url"`
|
||||
DatabaseVersion string `json:"database_version"`
|
||||
DatabaseLicense string `json:"database_license"`
|
||||
}
|
||||
|
||||
// Handle a GET /api/deployment request
|
||||
|
@ -61,11 +74,19 @@ func (s *Server) handleGetDeployments(c *gin.Context) {
|
|||
} else {
|
||||
result := make([]DeploymentInfo, len(depls))
|
||||
for i, d := range depls {
|
||||
version, license := d.DatabaseVersion()
|
||||
result[i] = DeploymentInfo{
|
||||
Name: d.Name(),
|
||||
Namespace: d.Namespace(),
|
||||
Mode: d.Mode(),
|
||||
PodCount: d.PodCount(),
|
||||
ReadyPodCount: d.ReadyPodCount(),
|
||||
VolumeCount: d.VolumeCount(),
|
||||
ReadyVolumeCount: d.ReadyVolumeCount(),
|
||||
StorageClasses: d.StorageClasses(),
|
||||
DatabaseURL: d.DatabaseURL(),
|
||||
DatabaseVersion: version,
|
||||
DatabaseLicense: license,
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
|
|
Loading…
Reference in a new issue