Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(xo-web/backup): smart mode preview should ignore xo:no-bak tags #7331

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
- [Backup/Restore] Don't count memory as a key (i.e. complete) disk [Forum#8212](https://xcp-ng.org/forum/post/69591) (PR [#7315](https://github.com/vatesfr/xen-orchestra/pull/7315))
- [Pool/patches] Disable Rolling Pool Update button if host is alone in its pool [#6415](https://github.com/vatesfr/xen-orchestra/issues/6415) (PR [#7286](https://github.com/vatesfr/xen-orchestra/pull/7286))
- [PIF] Fix IPv4 reconfiguration only worked when the IPv4 mode was updated (PR [#7324](https://github.com/vatesfr/xen-orchestra/pull/7324))
- [Backup/Smart mode] Make preview correctly ignoring `xo:no-bak` tags [Forum#69797](https://xcp-ng.org/forum/post/69797) (PR [#7331](https://github.com/vatesfr/xen-orchestra/pull/7331))

### Packages to release

Expand Down
50 changes: 0 additions & 50 deletions packages/xo-web/src/common/construct-query-string.js

This file was deleted.

23 changes: 15 additions & 8 deletions packages/xo-web/src/common/smart-backup/preview.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import _ from 'intl'
import PropTypes from 'prop-types'
import React from 'react'
import { createPredicate } from 'value-matcher'
import { createSelector } from 'reselect'
import { filter, map, pickBy } from 'lodash'
import { filter, map } from 'lodash'

import Component from './../base-component'
import constructQueryString from '../construct-query-string'
import Icon from './../icon'
import Link from './../link'
import renderXoItem from './../render-xo-item'
import Tooltip from './../tooltip'
import { Card, CardBlock, CardHeader } from './../card'
import { smartModeToComplexMatcher } from '../smartModeToComplexMatcher'

const SAMPLE_SIZE_OF_MATCHING_VMS = 3

Expand All @@ -21,18 +20,26 @@ export default class SmartBackupPreview extends Component {
vms: PropTypes.object.isRequired,
}

// user pattern completed with support for `xo:no-bak` tag automatically
// ignored by xo-server
_getComplexMatcher = createSelector(() => this.props.pattern, smartModeToComplexMatcher)

_getMatchingVms = createSelector(
() => this.props.vms,
createSelector(
() => this.props.pattern,
pattern => createPredicate(pickBy(pattern, val => val != null))
),
createSelector(this._getComplexMatcher, cm => cm.createPredicate()),
(vms, predicate) => filter(vms, predicate)
)

_getSampleOfMatchingVms = createSelector(this._getMatchingVms, vms => vms.slice(0, SAMPLE_SIZE_OF_MATCHING_VMS))

_getQueryString = createSelector(() => this.props.pattern, constructQueryString)
_getQueryString = createSelector(this._getComplexMatcher, cm => {
try {
return cm.toString()
} catch (error) {
console.error(error)
return ''
}
})

render() {
const nMatchingVms = this._getMatchingVms().length
Expand Down
60 changes: 60 additions & 0 deletions packages/xo-web/src/common/smartModeToComplexMatcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as CM from 'complex-matcher'
import escapeRegExp from 'lodash/escapeRegExp.js'

// compile a value-matcher like pattern (plus support for regexps) to a
// complex-matcher pattern
const pseudoValueToComplexMatcher = pattern => {
if (typeof pattern === 'string') {
return new CM.RegExpNode(`^${escapeRegExp(pattern)}$`, 'i')
}

if (Array.isArray(pattern)) {
return new CM.And(pattern.map(pseudoValueToComplexMatcher))
}

if (pattern instanceof RegExp) {
return new CM.RegExpNode(pattern)
}

if (pattern !== null && typeof pattern === 'object') {
const keys = Object.keys(pattern)
const { length } = keys

if (length === 1) {
const [key] = keys
if (key === '__and') {
return new CM.And(pattern.__and.map(pseudoValueToComplexMatcher))
}
if (key === '__or') {
return new CM.Or(pattern.__or.map(pseudoValueToComplexMatcher))
}
if (key === '__not') {
return new CM.Not(pseudoValueToComplexMatcher(pattern.__not))
}
}

const children = []
Object.keys(pattern).forEach(property => {
const subpattern = pattern[property]
if (subpattern !== undefined) {
children.push(new CM.Property(property, pseudoValueToComplexMatcher(subpattern)))
}
})
return children.length === 0 ? new CM.Null() : new CM.And(children)
}

throw new Error('could not transform this pattern')
}

export const smartModeToComplexMatcher = pattern => {
// don't mutate param
pattern = JSON.parse(JSON.stringify(pattern))

// if the pattern does not match expected entries, simply don't change it
const { tags } = pattern
if (tags !== undefined) {
;(tags.__not ?? tags.__and?.[1]?.__not)?.__or?.push(/^xo:no-bak(?:=.*)?$/)
}

return pseudoValueToComplexMatcher(pattern)
}
6 changes: 3 additions & 3 deletions packages/xo-web/src/common/zstd-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import React from 'react'

import _ from './intl'
import Component from './base-component'
import constructQueryString from './construct-query-string'
import Icon from './icon'
import Link from './link'
import Tooltip from './tooltip'
import { connectStore } from './utils'
import { createCollectionWrapper, createGetObjectsOfType, createSelector } from './selectors'
import { smartModeToComplexMatcher } from './smartModeToComplexMatcher'

@connectStore({
containers: createSelector(createGetObjectsOfType('pool'), createGetObjectsOfType('host'), (pools, hosts) => ({
Expand Down Expand Up @@ -41,11 +41,11 @@ export default class ZstdChecker extends Component {
pathname: '/home',
query: {
t: 'VM',
s: constructQueryString({
s: smartModeToComplexMatcher({
id: {
__or: vms,
},
}),
}).toString(),
},
}))

Expand Down
4 changes: 2 additions & 2 deletions packages/xo-web/src/xo-app/backup/overview/tab-jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import _ from 'intl'
import ActionButton from 'action-button'
import addSubscriptions from 'add-subscriptions'
import Button from 'button'
import constructQueryString from 'construct-query-string'
import Copiable from 'copiable'
import CopyToClipboard from 'react-copy-to-clipboard'
import decorate from 'apply-decorators'
Expand All @@ -21,6 +20,7 @@ import { get } from '@xen-orchestra/defined'
import { groupBy, isEmpty, map, some } from 'lodash'
import { injectState, provideState } from 'reaclette'
import { Proxy } from 'render-xo-item'
import { smartModeToComplexMatcher } from 'smartModeToComplexMatcher'
import { withRouter } from 'react-router'
import {
cancelJob,
Expand Down Expand Up @@ -360,7 +360,7 @@ class JobsTable extends React.Component {
handler: (job, { goTo }) =>
goTo({
pathname: '/home',
query: { t: 'VM', s: constructQueryString(job.vms) },
query: { t: 'VM', s: smartModeToComplexMatcher(job.vms).toString() },
}),
disabled: job => job.type !== 'backup',
label: _('redirectToMatchingVms'),
Expand Down