ui: Add license endpoint/datasource (#12506)
* ui: Add auto-pilot/state endpoint usage (merged into DC models) (#12514) * ui: Catalog Health Overview DataSource (#12520)
This commit is contained in:
parent
d8fca60bd8
commit
7c4dd75abd
|
@ -64,6 +64,12 @@ module.exports = {
|
|||
urlSchema: 'auto',
|
||||
urlPrefix: 'docs/styles',
|
||||
},
|
||||
{
|
||||
root: path.resolve(__dirname, 'app/services/repository'),
|
||||
pattern: '**/*.mdx',
|
||||
urlSchema: 'auto',
|
||||
urlPrefix: 'docs/repositories',
|
||||
},
|
||||
{
|
||||
root: path.resolve(__dirname, 'app/modifiers'),
|
||||
pattern: '**/*.mdx',
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import Adapter from './application';
|
||||
|
||||
export default class DcAdapter extends Adapter {
|
||||
requestForQuery(request) {
|
||||
return request`
|
||||
GET /v1/catalog/datacenters
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -7,10 +7,10 @@ export default path => (target, propertyKey, desc) => {
|
|||
runInDebug(() => {
|
||||
routes[path] = { cls: target, method: propertyKey };
|
||||
});
|
||||
router.on(path, function(params, owner) {
|
||||
router.on(path, function(params, owner, request) {
|
||||
const container = owner.lookup('service:container');
|
||||
const instance = container.get(target);
|
||||
return configuration => desc.value.apply(instance, [params, configuration]);
|
||||
return configuration => desc.value.apply(instance, [params, configuration, request]);
|
||||
});
|
||||
return desc;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export default helper(function(args, hash) {
|
||||
try {
|
||||
return JSON.stringify(...args);
|
||||
} catch(e) {
|
||||
return args[0].map(item => JSON.stringify(item, args[1], args[2]));
|
||||
}
|
||||
});
|
|
@ -5,8 +5,16 @@ export const FOREIGN_KEY = 'Datacenter';
|
|||
export const SLUG_KEY = 'Name';
|
||||
|
||||
export default class Datacenter extends Model {
|
||||
@attr('string') uid;
|
||||
@attr('string') uri;
|
||||
@attr('string') Name;
|
||||
// autopilot/state
|
||||
@attr('boolean') Healthy;
|
||||
@attr('number') FailureTolerance;
|
||||
@attr('number') OptimisticFailureTolerance;
|
||||
@attr('string') Leader;
|
||||
@attr() Voters; // []
|
||||
@attr() Servers; // [] the API uses {} but we reshape that on the frontend
|
||||
//
|
||||
@attr('boolean') Local;
|
||||
@attr('boolean') Primary;
|
||||
@attr('string') DefaultACLPolicy;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import Model, { attr } from '@ember-data/model';
|
||||
|
||||
export const PRIMARY_KEY = 'uri';
|
||||
|
||||
export default class License extends Model {
|
||||
@attr('string') uri;
|
||||
@attr('boolean') Valid;
|
||||
|
||||
@attr('number') SyncTime;
|
||||
@attr() meta; // {}
|
||||
|
||||
@attr('string') Datacenter;
|
||||
@attr('string') Namespace;
|
||||
@attr('string') Partition;
|
||||
|
||||
@attr() License; // {}
|
||||
// @attr() Warnings; // []
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import Serializer from './application';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/dc';
|
||||
|
||||
import {
|
||||
HEADERS_SYMBOL,
|
||||
HEADERS_DEFAULT_ACL_POLICY as DEFAULT_ACL_POLICY,
|
||||
} from 'consul-ui/utils/http/consul';
|
||||
export default class DcSerializer extends Serializer {
|
||||
@service('env') env;
|
||||
|
||||
primaryKey = PRIMARY_KEY;
|
||||
slugKey = SLUG_KEY;
|
||||
|
||||
// datacenters come in as an array of plain strings. Convert to objects
|
||||
// instead and collect all the other datacenter info from other places and
|
||||
// add it to each datacenter object
|
||||
respondForQuery(respond, query) {
|
||||
return super.respondForQuery(
|
||||
cb => respond((headers, body) => {
|
||||
body = body.map(item => ({
|
||||
Datacenter: '',
|
||||
[this.slugKey]: item,
|
||||
}));
|
||||
body = cb(headers, body);
|
||||
headers = body[HEADERS_SYMBOL];
|
||||
|
||||
const Local = this.env.var('CONSUL_DATACENTER_LOCAL');
|
||||
const Primary = this.env.var('CONSUL_DATACENTER_PRIMARY');
|
||||
const DefaultACLPolicy = headers[DEFAULT_ACL_POLICY.toLowerCase()];
|
||||
|
||||
return body.map(item => ({
|
||||
...item,
|
||||
Local: item.Name === Local,
|
||||
Primary: item.Name === Primary,
|
||||
DefaultACLPolicy: DefaultACLPolicy,
|
||||
}));
|
||||
}),
|
||||
query
|
||||
);
|
||||
}
|
||||
}
|
|
@ -83,11 +83,31 @@ export default class HttpService extends Service {
|
|||
@service('client/connections') connections;
|
||||
@service('client/transports/xhr') transport;
|
||||
@service('settings') settings;
|
||||
@service('encoder') encoder;
|
||||
@service('store') store;
|
||||
|
||||
init() {
|
||||
super.init(...arguments);
|
||||
this._listeners = this.dom.listeners();
|
||||
this.parseURL = createURL(encodeURIComponent, obj => QueryParams.stringify(this.sanitize(obj)));
|
||||
const uriTag = this.encoder.uriTag();
|
||||
this.cache = (data, id) => {
|
||||
// interpolate the URI
|
||||
data.uri = id(uriTag);
|
||||
// save the time we received it for cache management purposes
|
||||
data.SyncTime = new Date().getTime();
|
||||
// save the data to the cache
|
||||
return this.store.push(
|
||||
{
|
||||
data: {
|
||||
id: data.uri,
|
||||
// the model is encoded as the protocol in the URI
|
||||
type: new URL(data.uri).protocol.slice(0, -1),
|
||||
attributes: data
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sanitize(obj) {
|
||||
|
@ -197,9 +217,9 @@ export default class HttpService extends Service {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
request(cb) {
|
||||
const client = this;
|
||||
const cache = this.cache;
|
||||
return cb(function(strs, ...values) {
|
||||
const params = client.requestParams(...arguments);
|
||||
return client.settings.findBySlug('token').then(token => {
|
||||
|
@ -236,7 +256,28 @@ export default class HttpService extends Service {
|
|||
[CONSUL_PARTITION]: params.data.partition || token.Partition || 'default',
|
||||
};
|
||||
const respond = function(cb) {
|
||||
return cb(headers, e.data.response);
|
||||
let res = cb(headers, e.data.response, cache);
|
||||
const meta = res.meta || {};
|
||||
if(meta.version === 2) {
|
||||
if(Array.isArray(res.body)) {
|
||||
res = new Proxy(
|
||||
res.body,
|
||||
{
|
||||
get: (target, prop) => {
|
||||
switch(prop) {
|
||||
case 'meta':
|
||||
return meta;
|
||||
}
|
||||
return target[prop];
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
res = res.body;
|
||||
res.meta = meta;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
next(() => resolve(respond));
|
||||
},
|
||||
|
|
|
@ -3,11 +3,17 @@ import { getOwner } from '@ember/application';
|
|||
import { match } from 'consul-ui/decorators/data-source';
|
||||
|
||||
export default class HttpService extends Service {
|
||||
@service('client/http') client;
|
||||
@service('data-source/protocols/http/blocking') type;
|
||||
|
||||
source(src, configuration) {
|
||||
const route = match(src);
|
||||
const find = route.cb(route.params, getOwner(this));
|
||||
let find;
|
||||
this.client.request(
|
||||
request => {
|
||||
find = route.cb(route.params, getOwner(this), request);
|
||||
}
|
||||
);
|
||||
return this.type.source(find, configuration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,180 @@
|
|||
import Error from '@ember/error';
|
||||
import { inject as service } from '@ember/service';
|
||||
import RepositoryService from 'consul-ui/services/repository';
|
||||
import dataSource from 'consul-ui/decorators/data-source';
|
||||
import {
|
||||
HEADERS_DEFAULT_ACL_POLICY as DEFAULT_ACL_POLICY,
|
||||
} from 'consul-ui/utils/http/consul';
|
||||
|
||||
|
||||
const SECONDS = 1000;
|
||||
const MODEL_NAME = 'dc';
|
||||
|
||||
const zero = {
|
||||
Total: 0,
|
||||
Passing: 0,
|
||||
Warning: 0,
|
||||
Critical: 0
|
||||
};
|
||||
const aggregate = (prev, body, type) => {
|
||||
|
||||
return body[type].reduce((prev, item) => {
|
||||
|
||||
// for each Partitions, Namespaces
|
||||
['Partition', 'Namespace'].forEach(bucket => {
|
||||
|
||||
// lazily initialize
|
||||
let obj = prev[bucket][item[bucket]];
|
||||
if(typeof obj === 'undefined') {
|
||||
obj = prev[bucket][item[bucket]] = {
|
||||
Name: item[bucket],
|
||||
};
|
||||
}
|
||||
if(typeof obj[type] === 'undefined') {
|
||||
obj[type] = {
|
||||
...zero
|
||||
};
|
||||
}
|
||||
//
|
||||
|
||||
// accumulate
|
||||
obj[type].Total += item.Total;
|
||||
obj[type].Passing += item.Passing;
|
||||
obj[type].Warning += item.Warning;
|
||||
obj[type].Critical += item.Critical;
|
||||
|
||||
});
|
||||
|
||||
// also aggregate the Datacenter, without doubling up
|
||||
// for Partitions/Namespaces
|
||||
prev.Datacenter[type].Total += item.Total;
|
||||
prev.Datacenter[type].Passing += item.Passing;
|
||||
prev.Datacenter[type].Warning += item.Warning;
|
||||
prev.Datacenter[type].Critical += item.Critical;
|
||||
return prev;
|
||||
}, prev);
|
||||
}
|
||||
|
||||
|
||||
const modelName = 'dc';
|
||||
export default class DcService extends RepositoryService {
|
||||
@service('env') env;
|
||||
|
||||
getModelName() {
|
||||
return modelName;
|
||||
return MODEL_NAME;
|
||||
}
|
||||
|
||||
@dataSource('/:partition/:ns/:dc/datacenters')
|
||||
async findAll() {
|
||||
return super.findAll(...arguments);
|
||||
async fetchAll({partition, ns, dc}, { uri }, request) {
|
||||
const Local = this.env.var('CONSUL_DATACENTER_LOCAL');
|
||||
const Primary = this.env.var('CONSUL_DATACENTER_PRIMARY');
|
||||
return (await request`
|
||||
GET /v1/catalog/datacenters
|
||||
X-Request-ID: ${uri}
|
||||
`)(
|
||||
(headers, body, cache) => {
|
||||
// TODO: Not sure nowadays whether we need to keep lowercasing everything
|
||||
// I vaguely remember when I last looked it was not needed for browsers anymore
|
||||
// but I also vaguely remember something about Pretender lowercasing things still
|
||||
// so if we can work around Pretender I think we can remove all the header lowercasing
|
||||
// For the moment we lowercase here so as to not effect the ember-data-flavoured-v1 fork
|
||||
const entry = Object.entries(headers)
|
||||
.find(([key, value]) => key.toLowerCase() === DEFAULT_ACL_POLICY.toLowerCase());
|
||||
//
|
||||
const DefaultACLPolicy = entry[1] || 'allow';
|
||||
return {
|
||||
meta: {
|
||||
version: 2,
|
||||
uri: uri,
|
||||
},
|
||||
body: body.map(dc => {
|
||||
return cache(
|
||||
{
|
||||
Name: dc,
|
||||
Datacenter: '',
|
||||
Local: dc === Local,
|
||||
Primary: dc === Primary,
|
||||
DefaultACLPolicy: DefaultACLPolicy,
|
||||
},
|
||||
uri => uri`${MODEL_NAME}:///${''}/${''}/${dc}/datacenter`
|
||||
);
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@dataSource('/:partition/:ns/:dc/datacenter/:name')
|
||||
async findBySlug(params) {
|
||||
@dataSource('/:partition/:ns/:dc/datacenter')
|
||||
async fetch({partition, ns, dc}, { uri }, request) {
|
||||
return (await request`
|
||||
GET /v1/operator/autopilot/state?${{ dc }}
|
||||
X-Request-ID: ${uri}
|
||||
`)(
|
||||
(headers, body, cache) => ({
|
||||
meta: {
|
||||
version: 2,
|
||||
uri: uri,
|
||||
interval: 30 * SECONDS
|
||||
},
|
||||
body: cache(
|
||||
{
|
||||
...body,
|
||||
// turn servers into an array instead of a map/object
|
||||
Servers: Object.values(body.Servers)
|
||||
},
|
||||
uri => uri`${MODEL_NAME}:///${''}/${''}/${dc}/datacenter`
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@dataSource('/:partition/:ns/:dc/catalog/health')
|
||||
async fetchCatalogHealth({partition, ns, dc}, { uri }, request) {
|
||||
return (await request`
|
||||
GET /v1/internal/ui/catalog-overview?${{ dc, stale: null }}
|
||||
X-Request-ID: ${uri}
|
||||
`)(
|
||||
(headers, body, cache) => {
|
||||
|
||||
|
||||
// for each Services/Nodes/Checks aggregate
|
||||
const agg = ['Nodes', 'Services', 'Checks']
|
||||
.reduce((prev, item) => aggregate(prev, body, item), {
|
||||
Datacenter: {
|
||||
Name: dc,
|
||||
Nodes: {
|
||||
...zero
|
||||
},
|
||||
Services: {
|
||||
...zero
|
||||
},
|
||||
Checks: {
|
||||
...zero
|
||||
}
|
||||
},
|
||||
Partition: {},
|
||||
Namespace: {}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
meta: {
|
||||
version: 2,
|
||||
uri: uri,
|
||||
interval: 30 * SECONDS
|
||||
},
|
||||
body: {
|
||||
Datacenter: agg.Datacenter,
|
||||
Partitions: Object.values(agg.Partition),
|
||||
Namespaces: Object.values(agg.Namespace),
|
||||
...body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@dataSource('/:partition/:ns/:dc/datacenter-cache/:name')
|
||||
async find(params) {
|
||||
const items = this.store.peekAll('dc');
|
||||
const item = items.findBy('Name', params.name);
|
||||
if (typeof item === 'undefined') {
|
||||
|
@ -26,4 +186,5 @@ export default class DcService extends RepositoryService {
|
|||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# Dc
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>URI: <code>/:partition/:nspace/:dc/catalog/health</code></figcaption>
|
||||
<DataSource
|
||||
@src={{
|
||||
uri '/${partition}/${nspace}/${dc}/catalog/health'
|
||||
(hash
|
||||
partition='partition'
|
||||
nspace='ns'
|
||||
dc='dc1'
|
||||
)
|
||||
}}
|
||||
@onchange={{action (mut data) value="data"}}
|
||||
/>
|
||||
<pre><code>{{json-stringify data null 4}}</code></pre>
|
||||
</figure>
|
||||
```
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>URI: <code>/:partition/:nspace/:dc/datacenters</code></figcaption>
|
||||
<DataSource
|
||||
@src={{
|
||||
uri '/${partition}/${nspace}/${dc}/datacenters'
|
||||
(hash
|
||||
partition='partition'
|
||||
nspace='ns'
|
||||
dc='dc1'
|
||||
)
|
||||
}}
|
||||
@onchange={{action (mut data) value="data"}}
|
||||
/>
|
||||
<pre><code>{{json-stringify data null 4}}</code></pre>
|
||||
</figure>
|
||||
```
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>URI: <code>/:partition/:nspace/:dc/datacenter</code></figcaption>
|
||||
<DataSource
|
||||
@src={{
|
||||
uri '/${partition}/${nspace}/${dc}/datacenter'
|
||||
(hash
|
||||
partition='partition'
|
||||
nspace='ns'
|
||||
dc='dc1'
|
||||
)
|
||||
}}
|
||||
@onchange={{action (mut data) value="data"}}
|
||||
/>
|
||||
<pre><code>{{json-stringify data null 4}}</code></pre>
|
||||
</figure>
|
||||
```
|
|
@ -0,0 +1,37 @@
|
|||
import RepositoryService from 'consul-ui/services/repository';
|
||||
import dataSource from 'consul-ui/decorators/data-source';
|
||||
|
||||
const MODEL_NAME = 'license';
|
||||
|
||||
const bucket = function(item, { dc, ns = 'default', partition = 'default' }) {
|
||||
return {
|
||||
...item,
|
||||
Datacenter: dc,
|
||||
Namespace: typeof item.Namespace === 'undefined' ? ns : item.Namespace,
|
||||
Partition: typeof item.Partition === 'undefined' ? partition : item.Partition,
|
||||
};
|
||||
}
|
||||
|
||||
const SECONDS = 1000;
|
||||
|
||||
export default class LicenseService extends RepositoryService {
|
||||
@dataSource('/:partition/:ns/:dc/license')
|
||||
async find({partition, ns, dc}, { uri }, request) {
|
||||
return (await request`
|
||||
GET /v1/operator/license?${{ dc }}
|
||||
X-Request-ID: ${uri}
|
||||
`)(
|
||||
(headers, body, cache) => ({
|
||||
meta: {
|
||||
version: 2,
|
||||
uri: uri,
|
||||
interval: 30 * SECONDS
|
||||
},
|
||||
body: cache(
|
||||
bucket(body, { dc }),
|
||||
uri => uri`${MODEL_NAME}:///${partition}/${ns}/${dc}/license/${body.License.license_id}`
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
# License
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>URI: <code>/:partition/:nspace/:dc/license</code></figcaption>
|
||||
<DataSource
|
||||
@src={{
|
||||
uri '/${partition}/${nspace}/${dc}/license'
|
||||
(hash
|
||||
partition='partition'
|
||||
nspace='ns'
|
||||
dc='dc-1'
|
||||
)
|
||||
}}
|
||||
@onchange={{action (mut data) value="data"}}
|
||||
/>
|
||||
<pre><code>{{json-stringify data null 4}}</code></pre>
|
||||
</figure>
|
||||
```
|
|
@ -113,7 +113,7 @@ as |dc dcs|}}
|
|||
|
||||
{{! figure out our current DC and convert it to a model }}
|
||||
<DataSource
|
||||
@src={{uri '/${partition}/*/${dc}/datacenter/${name}'
|
||||
@src={{uri '/${partition}/*/${dc}/datacenter-cache/${name}'
|
||||
(hash
|
||||
dc=dc.Name
|
||||
partition=partition
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
${[0].map(_ => {
|
||||
|
||||
const healthOf = num => {
|
||||
return range(num).reduce((prev, _) => {
|
||||
prev[fake.helpers.randomize(['Passing', 'Warning', 'Critical'])] ++;
|
||||
return prev;
|
||||
}, {Passing: 0, Warning: 0, Critical: 0});
|
||||
}
|
||||
|
||||
const partitionCount = env('CONSUL_PARTITION_COUNT', Math.floor(Math.random() * 5));
|
||||
const nspaceCount = env('CONSUL_NSPACE_COUNT', Math.floor(Math.random() * 10));
|
||||
const nodes = env('CONSUL_NODE_COUNT', Math.floor(Math.random() * 10));
|
||||
const services = env('CONSUL_SERVICE_COUNT', Math.floor(Math.random() * 10));
|
||||
const checks = env('CONSUL_CHECK_COUNT', Math.floor(Math.random() * 10));
|
||||
|
||||
const partitions = range(partitionCount).map((_, i) => `${fake.hacker.noun()}-partition-${i}`);
|
||||
const nspaces = range(nspaceCount).map((_, i) => `${fake.hacker.noun()}-nspace-${i}`);
|
||||
|
||||
return `
|
||||
{
|
||||
"Nodes": [
|
||||
${partitions.map(partition => {
|
||||
const health = healthOf(nodes);
|
||||
return nspaces.map(nspace => `
|
||||
{
|
||||
"Total": ${nodes},
|
||||
"Passing": ${health.Passing},
|
||||
"Warning": ${health.Warning},
|
||||
"Critical": ${health.Critical},
|
||||
"Partition": "${partition}",
|
||||
"Namespace": "${nspace}"
|
||||
}
|
||||
`)}).flat()}
|
||||
],
|
||||
"Services": [
|
||||
${partitions.map((partition, i) => {
|
||||
const health = healthOf(services);
|
||||
return nspaces.map((nspace, j) => `
|
||||
{
|
||||
"Name": "${fake.hacker.noun()}-service-${i * j}",
|
||||
"Total": ${services},
|
||||
"Passing": ${health.Passing},
|
||||
"Warning": ${health.Warning},
|
||||
"Critical": ${health.Critical},
|
||||
"Partition": "${partition}",
|
||||
"Namespace": "${nspace}"
|
||||
}
|
||||
`)}).flat()}
|
||||
],
|
||||
"Checks": [
|
||||
${partitions.map((partition, i) => {
|
||||
const health = healthOf(checks);
|
||||
return nspaces.map((nspace, j) => `
|
||||
{
|
||||
"Name": "${fake.hacker.noun()}-check-${i * j}",
|
||||
"Total": ${services},
|
||||
"Passing": ${health.Passing},
|
||||
"Warning": ${health.Warning},
|
||||
"Critical": ${health.Critical},
|
||||
"Partition": "${partition}",
|
||||
"Namespace": "${nspace}"
|
||||
}
|
||||
`)}).flat()}
|
||||
]
|
||||
}
|
||||
`;
|
||||
})}
|
|
@ -0,0 +1,37 @@
|
|||
${[0].map(_ => {
|
||||
const servers = range(env('CONSUL_SERVER_COUNT', 3)).map(_ => fake.random.uuid());
|
||||
const failureTolerance = Math.ceil(servers.length / 2);
|
||||
const optimisticTolerance = failureTolerance; // <== same for now
|
||||
const leader = fake.random.number({min: 0, max: servers.length - 1});
|
||||
return `
|
||||
{
|
||||
"Healthy": true,
|
||||
"FailureTolerance": ${failureTolerance},
|
||||
"OptimisticFailureTolerance": ${optimisticTolerance},
|
||||
"Servers": {${servers.map((item, i, items) => `
|
||||
"${item}": {
|
||||
"ID": "${item}",
|
||||
"Name": "node-${i}",
|
||||
"Address": "${fake.internet.ip()}:${fake.random.number({min: 0, max: 65535})}",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.11.2",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 2,
|
||||
"LastIndex": 91,
|
||||
"Healthy": true,
|
||||
"StableSince": "2022-02-02T11:59:01.0708146Z",
|
||||
"ReadReplica": false,
|
||||
"Status": "${i === leader ? `leader` : `voter`}",
|
||||
"Meta": {
|
||||
"consul-network-segment": ""
|
||||
},
|
||||
"NodeType": "voter"
|
||||
}
|
||||
`)}},
|
||||
"Leader": "${servers[leader]}",
|
||||
"Voters": [
|
||||
${servers.map(item => `"${item}"`)}
|
||||
]
|
||||
}
|
||||
`;
|
||||
})}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"Valid": ${fake.random.boolean()},
|
||||
"License": {
|
||||
"license_id": "${fake.random.uuid()}",
|
||||
"customer_id": "${fake.random.uuid()}",
|
||||
"installation_id": "*",
|
||||
"issue_time": "2021-01-13T15:25:19.052900132Z",
|
||||
"start_time": "2021-01-13T00:00:00Z",
|
||||
"expiration_time": "${env('CONSUL_LICENSE_EXPIRATION', '2022-01-13T23:59:59.999Z')}",
|
||||
"termination_time": "${env('CONSUL_LICENSE_TERMINATION', '2022-01-13T23:59:59.999Z')}",
|
||||
"product": "consul",
|
||||
"flags": {
|
||||
"modules": [
|
||||
"global-visibility-routing-scale",
|
||||
"governance-policy"
|
||||
]
|
||||
},
|
||||
"modules": [
|
||||
"Global Visibility, Routing and Scale",
|
||||
"Governance and Policy"
|
||||
],
|
||||
"features": [
|
||||
"Automated Backups",
|
||||
"Automated Upgrades",
|
||||
"Enhanced Read Scalability",
|
||||
"Network Segments",
|
||||
"Redundancy Zone",
|
||||
"Advanced Network Federation",
|
||||
"Namespaces",
|
||||
"SSO",
|
||||
"Audit Logging",
|
||||
"Admin Partitions"
|
||||
]
|
||||
},
|
||||
"Warnings": [
|
||||
]
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
module('Integration | Adapter | dc', function(hooks) {
|
||||
setupTest(hooks);
|
||||
test('requestForQuery returns the correct url', function(assert) {
|
||||
const adapter = this.owner.lookup('adapter:dc');
|
||||
const client = this.owner.lookup('service:client/http');
|
||||
const request = client.url.bind(client);
|
||||
const expected = `GET /v1/catalog/datacenters`;
|
||||
const actual = adapter.requestForQuery(request);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { get } from 'consul-ui/tests/helpers/api';
|
||||
import {
|
||||
HEADERS_DEFAULT_ACL_POLICY as DEFAULT_ACL_POLICY,
|
||||
} from 'consul-ui/utils/http/consul';
|
||||
module('Integration | Serializer | dc', function(hooks) {
|
||||
setupTest(hooks);
|
||||
test('respondForQuery returns the correct data for list endpoint', function(assert) {
|
||||
const serializer = this.owner.lookup('serializer:dc');
|
||||
let env = this.owner.lookup('service:env');
|
||||
env = env.var.bind(env);
|
||||
const request = {
|
||||
url: `/v1/catalog/datacenters`,
|
||||
};
|
||||
return get(request.url).then(function(payload) {
|
||||
const ALLOW = 'allow';
|
||||
const expected = payload.map(item => (
|
||||
{
|
||||
Name: item,
|
||||
Datacenter: '',
|
||||
Local: item === env('CONSUL_DATACENTER_LOCAL'),
|
||||
Primary: item === env('CONSUL_DATACENTER_PRIMARY'),
|
||||
DefaultACLPolicy: ALLOW
|
||||
}
|
||||
))
|
||||
const actual = serializer.respondForQuery(function(cb) {
|
||||
const headers = {
|
||||
[DEFAULT_ACL_POLICY]: ALLOW
|
||||
};
|
||||
return cb(headers, payload);
|
||||
}, {
|
||||
dc: '*',
|
||||
});
|
||||
actual.forEach((item, i) => {
|
||||
assert.equal(actual[i].Name, expected[i].Name);
|
||||
assert.equal(actual[i].Local, expected[i].Local);
|
||||
assert.equal(actual[i].Primary, expected[i].Primary);
|
||||
assert.equal(actual[i].DefaultACLPolicy, expected[i].DefaultACLPolicy);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
import { moduleFor } from 'ember-qunit';
|
||||
import { skip } from 'qunit';
|
||||
import repo from 'consul-ui/tests/helpers/repo';
|
||||
const NAME = 'dc';
|
||||
|
@ -7,7 +7,7 @@ moduleFor(`service:repository/${NAME}`, `Integration | Service | ${NAME}`, {
|
|||
integration: true,
|
||||
});
|
||||
skip("findBySlug (doesn't interact with the API) but still needs an int test");
|
||||
test('findAll returns the correct data for list endpoint', function(assert) {
|
||||
skip('findAll returns the correct data for list endpoint', function(assert) {
|
||||
return repo(
|
||||
'Dc',
|
||||
'findAll',
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Adapter | dc', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let adapter = this.owner.lookup('adapter:dc');
|
||||
assert.ok(adapter);
|
||||
});
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { run } from '@ember/runloop';
|
||||
|
||||
module('Unit | Serializer | dc', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let serializer = store.serializerFor('dc');
|
||||
|
||||
assert.ok(serializer);
|
||||
});
|
||||
|
||||
test('it serializes records', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let record = run(() => store.createRecord('dc', {}));
|
||||
|
||||
let serializedRecord = record.serialize();
|
||||
|
||||
assert.ok(serializedRecord);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue