Skip to content

Commit

Permalink
fix(xo-web/backup): smart mode preview should ignore xo:no-bak tags
Browse files Browse the repository at this point in the history
  • Loading branch information
julien-f committed Jan 23, 2024
1 parent 1b0fc62 commit 4a4936a
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import * as CM from 'complex-matcher'
import escapeRegExp from 'lodash/escapeRegExp.js'

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

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

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

if (pattern !== null && typeof pattern === 'object') {
Expand All @@ -17,34 +23,25 @@ const valueToComplexMatcher = pattern => {
if (length === 1) {
const [key] = keys
if (key === '__and') {
return new CM.And(pattern.__and.map(valueToComplexMatcher))
return new CM.And(pattern.__and.map(pseudoValueToComplexMatcher))
}
if (key === '__or') {
return new CM.Or(pattern.__or.map(valueToComplexMatcher))
return new CM.Or(pattern.__or.map(pseudoValueToComplexMatcher))
}
if (key === '__not') {
return new CM.Not(valueToComplexMatcher(pattern.__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, valueToComplexMatcher(subpattern)))
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 default pattern => {
try {
return valueToComplexMatcher(pattern).toString()
} catch (error) {
console.warn('constructQueryString', pattern, error)
return ''
}
}
37 changes: 29 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 { pseudoValueToComplexMatcher } from '../_pseudoValueToComplexMatcher'

const SAMPLE_SIZE_OF_MATCHING_VMS = 3

Expand All @@ -21,18 +20,40 @@ 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,
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)
}
)

_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
2 changes: 1 addition & 1 deletion packages/xo-web/src/common/zstd-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react'

import _ from './intl'
import Component from './base-component'
import constructQueryString from './construct-query-string'
import constructQueryString from './_pseudoValueToComplexMatcher'
import Icon from './icon'
import Link from './link'
import Tooltip from './tooltip'
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 { pseudoValueToComplexMatcher } from '_pseudoValueToComplexMatcher'
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: pseudoValueToComplexMatcher(job.vms).toString() },
}),
disabled: job => job.type !== 'backup',
label: _('redirectToMatchingVms'),
Expand Down

0 comments on commit 4a4936a

Please sign in to comment.