Merge pull request #1325 from vincent99/master

Bugs & fleet
This commit is contained in:
Vincent Fiduccia 2020-09-21 03:34:10 -07:00 committed by GitHub
commit d6d43bfdce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 823 additions and 452 deletions

View File

@ -448,6 +448,12 @@ fleet:
cluster: Cluster
clusterGroup: Cluster Group
label: Deploy To
targetDisplay:
advanced: Advanced
cluster: "Cluster: {name}"
clusterGroup: "Group: {name}"
all: All
local: Local
workspace:
label: Workspace
clusterGroup:
@ -1065,6 +1071,7 @@ tableHeaders:
branch: Branch
builtIn: Built In
bundlesReady: Bundles
bundleDeploymentsReady: Deployments
chart: Chart
clusterCreatorDefault: Cluster Creator Default
clusterFlow: Cluster Flow

View File

@ -18,7 +18,7 @@ export default {
<style lang="scss">
.badge-state {
padding: 5px 10px;
padding: 2px 10px 1px 10px;
border: 1px solid transparent;
border-radius: 20px;
@ -47,8 +47,6 @@ export default {
display: inline-block;
max-width: 100%;
position: relative;
padding: 2px 10px 1px 10px;
font-size: 1em;
max-width: 110px;
font-size: .85em;
vertical-align: middle;

View File

@ -6,7 +6,7 @@ export default {
default: 'secondary'
},
label: {
type: String,
type: [String, Error],
default: null,
},
labelKey: {

View File

@ -25,11 +25,11 @@ export default {
return this.value?.details;
},
labels() {
return this.value?.metadata.labels || {};
return this.value?.labels || {};
},
annotations() {
return this.value?.metadata.annotations || {};
return this.value?.annotations || {};
},
description() {

View File

@ -8,7 +8,7 @@ const KEYS = {
outOfSync: 'warning',
pending: 'info',
waitApplied: 'info',
reqdy: 'success'
ready: 'success'
};
export default {

View File

@ -199,9 +199,8 @@ export default {
}
.badge-state {
margin-top: 4px;
padding: 2px 14px 1px 14px;
font-size: 10px;
font-size: 12px;
}
}

View File

@ -56,7 +56,7 @@ function realModeFor(query, id) {
export async function defaultAsyncData(ctx, resource, parentOverride) {
const { store, params, route } = ctx;
const inStore = store.getters['currentProduct'].inStore;
const inStore = store.getters['currentProduct']?.inStore;
// eslint-disable-next-line prefer-const
let { namespace, id } = params;

View File

@ -146,21 +146,17 @@ export function init(store) {
headers(WORKLOAD_TYPES.REPLICATION_CONTROLLER, [STATE, NAMESPACE_NAME, 'Ready', 'Current', 'Desired', AGE]);
headers(POD, [STATE, NAMESPACE_NAME, 'Ready', 'Restarts', 'IP', NODE_COL, AGE]);
// These look to be for [Cluster]RoleTemplate, not [Cluster]Role.
// headers(RBAC.ROLE, [
// STATE,
// NAME_COL,
// BUILT_IN,
// AGE
// ]);
headers(RBAC.ROLE, [
STATE,
NAMESPACE_NAME,
AGE
]);
// headers(RBAC.CLUSTER_ROLE, [
// STATE,
// NAME_COL,
// BUILT_IN,
// CLUSTER_CREATOR_DEFAULT,
// AGE
// ]);
headers(RBAC.CLUSTER_ROLE, [
STATE,
NAME_COL,
AGE
]);
virtualType({
label: 'Cluster Dashboard',

View File

@ -106,6 +106,7 @@ export const MANAGEMENT = {
// Base: /k8s/clusters/<id>/v1/
export const FLEET = {
BUNDLE: 'fleet.cattle.io.bundle',
CLUSTER: 'fleet.cattle.io.cluster',
CLUSTER_GROUP: 'fleet.cattle.io.clustergroup',
GIT_REPO: 'fleet.cattle.io.gitrepo',

View File

@ -48,8 +48,7 @@ export default {
res.stateBackground = colorForState(res.bundleState).replace('text-', 'bg-');
res.stateDisplay = res.bundleState;
for ( const stat of res.modifiedStatus || []) {
stat.action = ( stat.missing ? 'Create' : ( stat.delete ? 'Remove' : 'Update' ) );
for ( const stat of res.nonReadyStatus || []) {
stat.id = `row${ i++ }`;
}
}
@ -60,9 +59,16 @@ export default {
unreadyHeaders() {
return [
{
name: 'action',
value: 'action',
label: 'Action',
name: 'state',
value: 'summary.State',
label: 'State',
formatter: 'BadgeStateFormatter',
formatterOpts: { arbitrary: true }
},
{
name: 'kind',
value: 'kind',
label: 'Resource',
},
// {
// name: 'apiVersion',
@ -74,11 +80,6 @@ export default {
value: 'namespace',
label: 'Namespace',
},
{
name: 'kind',
value: 'kind',
label: 'Resource',
},
{
name: 'name',
value: 'name',
@ -104,7 +105,7 @@ export default {
<h3 class="inline-block">
{{ res.name }}
</h3>
<BadgeState class="pull-right" :value="res" />
<BadgeState class="ml-10" :value="res" />
</div>
<Banner
@ -114,7 +115,7 @@ export default {
/>
<SortableTable
:rows="res.modifiedStatus"
:rows="res.nonReadyStatus"
:headers="unreadyHeaders"
:table-actions="false"
:row-actions="false"

View File

@ -0,0 +1,113 @@
<script>
import Loading from '@/components/Loading';
import SimpleBox from '@/components/SimpleBox';
import BadgeState from '@/components/BadgeState';
import Banner from '@/components/Banner';
import SortableTable from '@/components/SortableTable';
import FleetSummary from '@/components/FleetSummary';
import { clone } from '@/utils/object';
import { colorForState } from '@/plugins/steve/resource-instance';
export default {
name: 'DetailGitRepo',
components: {
Loading,
BadgeState,
Banner,
FleetSummary,
SimpleBox,
SortableTable,
},
props: {
value: {
type: Object,
required: true,
},
},
computed: {
unready() {
let i = 1;
const out = clone(this.value.status?.summary?.nonReadyResources || []);
for ( const res of out ) {
res.stateBackground = colorForState(res.bundleState).replace('text-', 'bg-');
res.stateDisplay = res.bundleState;
for ( const stat of res.nonReadyResources || []) {
stat.id = `row${ i++ }`;
}
}
return out;
},
unreadyHeaders() {
return [
{
name: 'state',
value: 'summary.State',
label: 'State',
},
// {
// name: 'apiVersion',
// value: 'apiVersion',
// label: 'API Version',
// },
{
name: 'namespace',
value: 'namespace',
label: 'Namespace',
},
{
name: 'kind',
value: 'kind',
label: 'Resource',
},
{
name: 'name',
value: 'name',
label: 'Name',
},
];
}
},
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<div v-else>
<h2 v-t="'fleet.cluster.summary'" class="mt-20" />
<FleetSummary :value="value.status.summary" />
<hr class="mt-20 mb-20" />
<h2 v-t="'fleet.cluster.nonReady'" />
<SimpleBox v-for="(res, idx) in unready" :key="idx">
<div class="clearfix">
<h3 class="inline-block">
{{ res.name }}
</h3>
<BadgeState class="pull-right" :value="res" />
</div>
<Banner
v-if="res.message"
:color="res.stateBackground.replace(/bg-/, '')"
:label="res.message"
/>
<SortableTable
:rows="res.modifiedStatus"
:headers="unreadyHeaders"
:table-actions="false"
:row-actions="false"
:search="false"
key-field="id"
/>
</SimpleBox>
</div>
</template>

View File

@ -1,108 +0,0 @@
<script>
import RbacPermissions from '@/components/RbacPermissions';
import CreateEditView from '@/mixins/create-edit-view';
import NameNsDescription from '@/components/form/NameNsDescription';
import Footer from '@/components/form/Footer';
import LabeledInput from '@/components/form/LabeledInput';
import RadioGroup from '@/components/form/RadioGroup';
import ResourceTabs from '@/components/form/ResourceTabs';
/**
* Edit view for RBAC Cluster Role
@displayName Edit RBAC Cluster Role
*/
export default {
name: 'CruClusterRole',
components: {
Footer,
LabeledInput,
NameNsDescription,
RadioGroup,
RbacPermissions,
ResourceTabs
},
mixins: [CreateEditView],
data() {
const radioOptions = [true, false];
const lockedLabels = ['Yes, New bindings are not allowed to use this role', 'No'];
const newUserDefault = ['Yes, Default role for new cluster creation', 'No'];
return {
lockedLabels,
newUserDefault,
radioOptions,
};
},
methods: {
/**
* Sends the user back to cluster roles page after save is finished
*/
done() {
this.$router.replace({ name: 'rbac-roles-cluster' });
}
},
};
</script>
<template>
<form>
<NameNsDescription
:value="value"
:mode="mode"
name-label="Name"
:register-before-hook="registerBeforeHook"
>
<template v-slot:namespace>
<LabeledInput
key="name"
v-model="value.metadata.name"
label="Name"
:mode="mode"
/>
</template>
</NameNsDescription>
<div class="spacer"></div>
<div class="row">
<div class="col span-6">
<RadioGroup
v-model="value.locked"
name="locked"
label="Locked"
:options="radioOptions"
:labels="lockedLabels"
/>
</div>
<div class="col span-6">
<RadioGroup
v-model="value.clusterCreatorDefault"
label="Cluster Creator Default"
name="clusterCreatorDefault"
:options="radioOptions"
:labels="newUserDefault"
/>
</div>
</div>
<div class="spacer"></div>
<section>
<div class="row">
<RbacPermissions
v-model="value.rules"
:mode="mode"
label="Grant Resources"
btn-label="Grant Resource"
/>
</div>
</section>
<ResourceTabs v-model="value" :mode="mode" />
<Footer :mode="mode" :errors="errors" @save="save" @done="done" />
</form>
</template>

View File

@ -1,115 +0,0 @@
<script>
import RbacPermissions from '@/components/RbacPermissions';
import CreateEditView from '@/mixins/create-edit-view';
import NameNsDescription from '@/components/form/NameNsDescription';
import Footer from '@/components/form/Footer';
import RadioGroup from '@/components/form/RadioGroup';
import ResourceTabs from '@/components/form/ResourceTabs';
/**
* Edit view for RBAC Role
@displayName Edit RBAC Role
*/
export default {
name: 'CruRole',
components: {
Footer,
NameNsDescription,
RadioGroup,
RbacPermissions,
ResourceTabs
},
mixins: [CreateEditView],
data() {
const radioOptions = [true, false];
const lockedLabels = ['Yes, New bindings are not allowed to use this role', 'No'];
const newUserDefault = ['Yes, Default role for new user creation', 'No'];
return {
lockedLabels,
newUserDefault,
radioOptions,
};
},
methods: {
/**
* Sends the user back to roles page after save is finished
*/
done() {
this.$router.replace({ name: 'rbac-roles' });
}
},
};
</script>
<template>
<form>
<NameNsDescription
:value="value"
:mode="mode"
name-label="Name"
:register-before-hook="registerBeforeHook"
/>
<div class="spacer"></div>
<section class="row">
<div class="col span-6">
<label for="rbac-create-type">
Type
</label>
<div id="rbac-create-type">
{{ value.type }}
</div>
</div>
</section>
<div class="spacer"></div>
<section class="row">
<div class="col span-6">
<RadioGroup
v-model="value.locked"
name="locked"
label="Locked"
:options="radioOptions"
:labels="lockedLabels"
/>
</div>
<div class="col span-6">
<RadioGroup
v-model="value.newUserDefault"
name="newUserDefault"
label="New User Default"
:options="radioOptions"
:labels="newUserDefault"
/>
</div>
</section>
<div class="spacer"></div>
<section>
<div class="row">
<RbacPermissions
v-model="value.rules"
:mode="mode"
label="Grant Resources"
btn-label="Grant Resource"
/>
</div>
</section>
<ResourceTabs v-model="value" :mode="mode" />
<Footer
:mode="mode"
:errors="errors"
@save="save"
@done="done"
/>
</form>
</template>

View File

@ -71,15 +71,20 @@ export default {
},
async fetch() {
const hash = await allHash({
const requests = {
configMaps: this.$store.dispatch('cluster/findAll', { type: CONFIG_MAP }),
secrets: this.$store.dispatch('cluster/findAll', { type: SECRET }),
nodes: this.$store.dispatch('cluster/findAll', { type: NODE }),
services: this.$store.dispatch('cluster/findAll', { type: SERVICE }),
pvcs: this.$store.dispatch('cluster/findAll', { type: PVC })
});
};
this.allSecrets = hash.secrets;
if ( this.$store.getters['cluster/schemaFor'](SECRET) ) {
requests.secrets = this.$store.dispatch('cluster/findAll', { type: SECRET });
}
const hash = await allHash(requests);
this.allSecrets = hash.secrets || [];
this.allConfigMaps = hash.configMaps;
this.allNodes = hash.nodes.map(node => node.id);
this.headlessServices = hash.services.filter(service => service.spec.clusterIP === 'None');

View File

@ -0,0 +1,58 @@
<script>
import ResourceTable from '@/components/ResourceTable';
import {
AGE,
STATE,
NAME,
} from '@/config/table-headers';
export default {
name: 'ListBundle',
components: { ResourceTable },
props: {
schema: {
type: Object,
required: true,
},
rows: {
type: Array,
required: true,
},
},
computed: {
headers() {
const out = [
STATE,
NAME,
{
name: 'deploymentsReady',
labelKey: 'tableHeaders.bundleDeploymentsReady',
value: 'status.display.readyClusters',
sort: 'status.display.readyClusters',
search: ['status.summary.ready', 'status.summary.desiredReady'],
},
AGE
];
return out;
},
},
};
</script>
<template>
<ResourceTable
:schema="schema"
:headers="headers"
:rows="rows"
>
<template #cell:deploymentsReady="{row}">
<span v-if="row.status.summary.desiredReady != row.status.summary.ready" class="text-warning">
{{ row.status.summary.ready }}/{{ row.status.summary.desiredReady }}</span>
<span v-else>{{ row.status.summary.desiredReady }}</span>
</template>
</ResourceTable>
</template>

View File

@ -139,18 +139,7 @@ export default {
</template>
<template #cell:target="{row}">
<template v-if="row.targetInfo.mode === 'cluster'">
Cluster: {{ row.targetInfo.cluster }}
</template>
<template v-else-if="row.targetInfo.mode === 'clusterGroup'">
Group: {{ row.targetInfo.clusterGroup }}
</template>
<template v-else-if="row.targetInfo.mode === 'local'">
Local
</template>
<template v-else>
Advanced
</template>
{{ row.targetInfo.modeDisplay }}
</template>
</SortableTable>
</template>

View File

@ -0,0 +1,24 @@
import { escapeHtml } from '@/utils/string';
export default {
deploymentInfo() {
const ready = this.status?.summary?.ready || 0;
const total = this.status?.summary?.desiredReady || 0;
return {
ready,
unready: total - ready,
total,
};
},
groupByLabel() {
const name = this.metadata.namespace;
if ( name ) {
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.workspace', { name: escapeHtml(name) });
} else {
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
}
},
};

View File

@ -0,0 +1,13 @@
import { escapeHtml } from '@/utils/string';
export default {
groupByLabel() {
const name = this.metadata.namespace;
if ( name ) {
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.workspace', { name: escapeHtml(name) });
} else {
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
}
},
};

View File

@ -23,7 +23,7 @@ export default {
},
github() {
const match = this.spec.repo.match(/^https?:\/\/github\.com\/(.*)(\.git)?$/);
const match = this.spec.repo.match(/^https?:\/\/github\.com\/(.*?)(\.git)?\/*$/);
if ( match ) {
return match[1];
@ -43,6 +43,7 @@ export default {
repo = repo.replace(/.git$/, '');
repo = repo.replace(/^https:\/\//, '');
repo = repo.replace(/\/+$/, '');
if ( this.github ) {
return this.github;
@ -88,7 +89,7 @@ export default {
if ( this.metadata.namespace === 'fleet-local' ) {
mode = 'local';
} else if ( !targets.length ) {
mode = 'cluster';
mode = 'all';
} else if ( targets.length === 1) {
const target = targets[0];
@ -122,7 +123,7 @@ export default {
return {
mode,
modeDisplay: this.t(`fleet.gitRepo.targetMode."${ mode }"`),
modeDisplay: this.t(`fleet.gitRepo.targetDisplay."${ mode }"`, { name: cluster || clusterGroup || '?' }),
cluster,
clusterGroup,
advanced

View File

@ -62,7 +62,7 @@
"lodash": "^4.17.15",
"marked": "^1.1.1",
"node-sass": "^4.12.0",
"nuxt": "2.13.3",
"nuxt": "2.14.5",
"portal-vue": "^2.1.5",
"require-extension-hooks": "^0.3.3",
"require-extension-hooks-babel": "^1.0.0-beta.1",

View File

@ -86,8 +86,8 @@ export default {
this.value = await this.$store.dispatch('cluster/create', {
type: 'chartInstallAction',
metadata: {
namespace: releaseNamespace,
name: releaseName
namespace: this.existing ? this.existing.spec.namespace : releaseNamespace,
name: this.existing ? this.existing.spec.name : releaseName,
}
});
@ -572,7 +572,7 @@ export default {
},
actionInput(isUpgrade) {
const fromChart = this.versionInfo.values || {};
const fromChart = this.versionInfo?.values || {};
if ( !this.valuesComponent && !this.hasQuestions ) {
try {

View File

@ -129,13 +129,17 @@ export default {
},
// Fuzzy is only for plugins/lookup, do not use in real code
schemaFor: (state, getters) => (type, fuzzy = false) => {
schemaFor: (state, getters) => (type, fuzzy = false, allowThrow = true) => {
const schemas = state.types[SCHEMA];
type = normalizeType(type);
if ( !schemas ) {
throw new Error("Schemas aren't loaded yet");
if ( allowThrow ) {
throw new Error("Schemas aren't loaded yet");
} else {
return null;
}
}
const out = schemas.map.get(type);
@ -225,6 +229,10 @@ export default {
if ( !revision ) {
const cache = state.types[type];
if ( !cache ) {
return null;
}
for ( const obj of cache.list ) {
if ( obj && obj.metadata ) {
const neu = parseInt(obj.metadata.resourceVersion, 10);

View File

@ -51,6 +51,7 @@ const DNS_LIKE_TYPES = ['dnsLabel', 'dnsLabelRestricted', 'hostname'];
const REMAP_STATE = {
disabled: 'inactive',
notapplied: 'Not Applied',
notready: 'Not Ready',
};
const DEFAULT_COLOR = 'warning';
@ -814,12 +815,16 @@ export default {
await this.$dispatch('load', { data: res, existing: (forNew ? this : undefined ) });
}
} catch (e) {
// Things went poorly, probably a conflict, try to load the new version
await this.$dispatch('find', {
type: this.type,
id: this.id,
opt: { force: true }
});
if ( this.type && this.id && e?._status === 409) {
// If there's a conflict, try to load the new version
await this.$dispatch('find', {
type: this.type,
id: this.id,
opt: { force: true }
});
}
return Promise.reject(e);
}
return this;

View File

@ -139,16 +139,20 @@ export const getters = {
namespaces(state, getters) {
return () => {
const inStore = getters['currentProduct'].inStore;
const clusterId = getters['currentCluster'].id;
const namespaces = getters[`${ inStore }/all`](NAMESPACE);
const out = {};
const inStore = getters['currentProduct']?.inStore;
const clusterId = getters['currentCluster']?.id;
if ( !clusterId || !inStore ) {
return out;
}
const namespaces = getters[`${ inStore }/all`](NAMESPACE);
const filters = state.namespaceFilters.filter(x => !x.startsWith('namespaced://'));
const includeAll = getters.isAllNamespaces;
const includeSystem = filters.includes('all://system');
const includeUser = filters.includes('all://user') || filters.length === 0;
const includeOrphans = filters.includes('all://orphans');
const out = {};
// Special cases to pull in all the user, system, or orphaned namespaces
if ( includeAll || includeOrphans || includeSystem || includeUser ) {

688
yarn.lock

File diff suppressed because it is too large Load Diff