podaffinityterm weight

This commit is contained in:
Nancy Butler 2023-04-17 13:07:38 -07:00
parent 04c8b55814
commit dd5b6c1f35
11 changed files with 350 additions and 72 deletions

View File

@ -154,12 +154,15 @@ export default Component.extend({
}
}),
nodeAffinity: computed('value.nodeAffinity.{preferredDuringSchedulingIgnoredDuringExecution,requiredDuringSchedulingIgnoredDuringExecution}', {
nodeAffinity: computed('value.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution', 'value.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms', {
get(){
if (!this.value?.nodeAffinity){
set(this.value, 'nodeAffinity', {})
}
this.initPreferredRequired(this.value.nodeAffinity)
this.initPreferredRequired(this.value.nodeAffinity, true)
if (!this.value.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms){
set(this.value, 'nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms', [])
}
return get(this.value, 'nodeAffinity')
},
@ -170,11 +173,13 @@ export default Component.extend({
return term
}),
requiredDuringSchedulingIgnoredDuringExecution: (val.requiredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: (val?.requiredDuringSchedulingIgnoredDuringExecution?.nodeSelectorTerms || []).map((term) => {
delete term._id;
return term
})
return term
})
}
}
set(this.value, 'nodeAffinity', withoutId)
@ -184,11 +189,13 @@ export default Component.extend({
}
}),
initPreferredRequired: (val) => {
initPreferredRequired: (val, isNode) => {
if (!val.preferredDuringSchedulingIgnoredDuringExecution){
set(val, 'preferredDuringSchedulingIgnoredDuringExecution', [])
}
if (!val.requiredDuringSchedulingIgnoredDuringExecution){
if (!val.requiredDuringSchedulingIgnoredDuringExecution && isNode){
set(val, 'requiredDuringSchedulingIgnoredDuringExecution', { nodeSelectorTerms: [] })
} else {
set(val, 'requiredDuringSchedulingIgnoredDuringExecution', [])
}
}

View File

@ -2,7 +2,7 @@
<div class="row">
<FormNodeAffinityK8s
@mode={{mode}}
@value={{nodeAffinity}}
@nodeAffinity={{nodeAffinity}}
/>
</div>
<div class="row">
@ -11,6 +11,7 @@
@value={{podAffinity}}
@podAffinity={{podAffinity}}
@podAntiAffinity={{podAntiAffinity}}
{{!-- //TODO nb used? --}}
@update={{action "affinityChanged"}}
/>
</div>

View File

@ -1,4 +1,137 @@
import Component from '@ember/component';
import layout from './template';
import {
computed,
get,
set,
observer
} from '@ember/object';
import { randomStr } from '../../utils/util';
import { TERM_PRIORITY } from '../form-pod-affinity-k8s/component';
/**
* nodeAffinity: {
* preferredDuringSchedulingIgnoredDuringExecution:
* - {
* preference: nodeselectorterm,
* weight: int
* }
* requiredDuringSchedulingIgnoredDuringExecution:
* nodeSelectorTerms:
* - nodeselectorterm
* }
*
*
* nodeselectorterm: {
* matchexpressions:
* - {
* key: string,
* operator: string, one of: [In, NotIn, Exists, DoesNotExist Gt, Lt]
* value: string array ... If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer.
* }
* matchfields: same as match expressions but matches fields instead of labels
* }
*/
export default Component.extend({ layout })
export default Component.extend({
layout,
TERM_PRIORITY,
nodeAffinity: null,
mode: 'new',
allTerms: [],
/**
* this component renders one list for required & preferred arrays of node selector terms
* each nodeaffinitytermk8s component can change between required and perferred
* the overall list shouldn't re-order when a term is moved to a different underlying array so rather than computing this off the arrays in spec
* this list will track which array a term should belong to and the arrays in spec will be computed off this
*
* list of all terms:
* - {
* priority: preferred/required
* term:preferred or required term
* }
*
*/
init(){
// TODO nb why is required... an array navigating back from view as yaml?
this._super(...arguments);
const allTerms = []
const addTerms = function(terms = [], priority){
terms.forEach((term) => {
allTerms.push({
priority,
term
})
})
}
addTerms(this.nodeAffinity?.preferredDuringSchedulingIgnoredDuringExecution, TERM_PRIORITY.PREFERRED)
addTerms(this.nodeAffinity?.requiredDuringSchedulingIgnoredDuringExecution?.nodeSelectorTerms, TERM_PRIORITY.REQUIRED)
set(this, 'allTerms', allTerms)
},
actions: {
addTerm(){
const neu = {
priority: TERM_PRIORITY.REQUIRED,
term: { _id: randomStr() }
}
this.allTerms.push(neu)
get(this.nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms').addObject(neu.term)
this.notifyPropertyChange('allTerms')
this.notifyPropertyChange('nodeAffinity')
},
removeTerm(term){
// TODO nb does removeObject work here? Will it work in typeChanged?
get(this, 'allTerms').removeObject(term)
if (term.priority === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms')
removeFrom.removeObject(term.term)
} else {
const removeFrom = get(this.nodeAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
removeFrom.removeObject(term.term)
}
this.notifyPropertyChange('nodeAffinity')
},
typeChanged(term, old){
// TODO nb add weight
if (old === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms')
const addTo = get(this.nodeAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
set(this.nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
} else {
// TODO nb remove weight
const removeFrom = get(this.nodeAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms')
set(this.nodeAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
}
this.notifyPropertyChange('nodeAffinity')
},
},
editing: computed('mode', function() {
const mode = get(this, 'mode')
return mode === 'new' || mode === 'edit'
}),
removeFromSpec: (term, array) => {
return array.filter((each) => each._id !== term._id)
},
})

View File

@ -1,3 +1,29 @@
<div>
node affinity component
<div class="box">
<div>
<label class="acc-label">
{{t "clusterNew.agentConfig.overrideAffinity.nodeAffinity.title"}}
</label>
</div>
<div class="row">
<div class="col span-12">
<div>
{{#each allTerms as |term|}}
<FormNodeSelectorTermK8s
@value={{term}}
@mode={{mode}}
@remove={{action "removeTerm" term}}
@typeChanged={{action "typeChanged" term}}
/>
{{/each}}
</div>
{{#if editing}}
<button class="btn bg-link icon-btn" type="button" {{action "addTerm"}}>
<i class="icon icon-plus text-small"/>
<span>
{{t "clusterNew.agentConfig.overrideAffinity.nodeAffinity.addTerm"}}
</span>
</button>
{{/if}}
</div>
</div>
</div>

View File

@ -1,4 +1,21 @@
import Component from '@ember/component';
import layout from './template';
import { priorityOptions } from '../form-pod-affinity-term-k8s/component';
export default Component.extend({ layout })
export default Component.extend({
layout,
value: null,
mode: 'new',
priorityOptions,
actions: {
removeTerm(){
if (this.remove){
this.remove()
}
}
},
})

View File

@ -0,0 +1,42 @@
<div class="box mb-10 affinity-term">
{{#if editing}}
<div class="affinity-remove">
<button class="btn btn-sm " type="button" {{action "removeTerm"}}>
<i class="icon icon-x"/>
</button>
</div>
{{/if }}
<div class="row mt-10">
<div class="col span-6">
{{!-- <label class="acc-label">
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.priority"}}
</label> --}}
<NewSelect
@class="form-control"
@value={{priority}}
@optionLabelPath="label"
@optionValuePath="value"
@localizedLabel={{true}}
@content={{priorityOptions}}
/>
</div>
</div>
<div class="row">
{{#if (eq value.priority TERM_PRIORITY.PREFERRED)}}
<div class="col span-6">
<label class="acc-label">{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.weight.label"}}</label>
{{#input-or-display
editable=editing
value=toplogyKey
}}
{{input
type="text"
class="form-control input-sm"
value=weight
placeholder=(t "clusterNew.agentConfig.overrideAffinity.podAffinity.weight.placeholder")
}}
{{/input-or-display}}
</div>
{{/if}}
</div>
</div>

View File

@ -33,7 +33,6 @@ export default Component.extend({
podAntiAffinity: null,
value: null,
mode: 'new',
anti: false,
allTerms: [],
/**
* this component renders one list for required & preferred arrays of terms in affinity & antiAffinity
@ -70,13 +69,6 @@ export default Component.extend({
},
actions: {
addRequired(){
this.requiredDuringSchedulingIgnoredDuringExecution.pushObject({})
},
addPreferred(){
this.preferredDuringSchedulingIgnoredDuringExecution.pushObject({})
},
addTerm(){
const neu = {
priority: TERM_PRIORITY.REQUIRED,
@ -92,6 +84,7 @@ export default Component.extend({
removeTerm(term){
// TODO nb update relevent spec array
// TODO nb does removeObject work here? Will it work in typeChanged and antiChanged?
get(this, 'allTerms').removeObject(term)
if (term.anti){
if (term.priority === TERM_PRIORITY.REQUIRED){
@ -118,44 +111,48 @@ export default Component.extend({
}
},
affinityChanged(){
// TODO nb this.update()
// needed?
},
typeChanged(term, old){
if (term.anti){
// TODO nb add 'weight'
if (old === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
set(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
// preferred terms are an object with weight and podAffinityTerm fields; required terms are just the contents of podAffinityTerm
set(term, 'term', this.addWeight(term.term))
addTo.pushObject(term.term)
} else {
// TODO nb remove weight
const removeFrom = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
set(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
set(term, 'term', this.removeWeight(term))
addTo.pushObject(term.term)
}
this.notifyPropertyChange('podAntiAffinity')
} else {
// TODO nb add 'weight'
if (old === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
set(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
set(term, 'term', this.addWeight(term.term))
addTo.pushObject(term.term)
} else {
// TODO nb remove weight
const removeFrom = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
set(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
set(term, 'term', this.removeWeight(term))
addTo.pushObject(term.term)
}
this.notifyPropertyChange('podAffinity')
@ -211,4 +208,25 @@ export default Component.extend({
return array.filter((each) => each._id !== term._id)
},
addWeight: (term) => {
const out = {
// weight: null,
podAffinityTerm: term,
_id: term._id
}
delete out.podAffinityTerm._id
return out
},
removeWeight: (term) => {
const out = {
_id: term._id,
...term.podAffinityTerm
}
return out
}
})

View File

@ -1,11 +1,7 @@
<div class="box">
<div>
<label class="acc-label">
{{#if anti}}
{{t "clusterNew.agentConfig.overrideAffinity.podAntiAffinity.title"}}
{{else}}
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.title"}}
{{/if}}
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.title"}}
</label>
</div>
<div class="row">

View File

@ -42,11 +42,20 @@ const namespaceModes = {
IN_LIST: 'inList'
}
export const priorityOptions = [{
value: TERM_PRIORITY.REQUIRED,
label: 'clusterNew.agentConfig.overrideAffinity.podAffinity.typeOptions.required'
}, {
value: TERM_PRIORITY.PREFERRED,
label: 'clusterNew.agentConfig.overrideAffinity.podAffinity.typeOptions.preferred'
}]
export default Component.extend({
layout,
namespaceModes,
priorityOptions,
TERM_PRIORITY,
/**
* {
* priority: preferred or required
@ -64,13 +73,6 @@ export default Component.extend({
typeChanged: null,
antiChanged: null,
priorityOptions: [{
value: TERM_PRIORITY.REQUIRED,
label: 'clusterNew.agentConfig.overrideAffinity.podAffinity.typeOptions.required'
}, {
value: TERM_PRIORITY.PREFERRED,
label: 'clusterNew.agentConfig.overrideAffinity.podAffinity.typeOptions.preferred'
}],
affinityOptions: [{
value: 'true',
@ -88,27 +90,44 @@ export default Component.extend({
}
},
// namespaceModeObserver: observer('namespaceMode', () => {
// }),
editing: computed('mode', function() {
const mode = get(this, 'mode')
return mode === 'new' || mode === 'edit'
}),
term: computed('value.term', function(){
return get(this.value, 'term') || {}
// TODO nb acccount for weight if value.priority === 'preferred'
podAffintyTerm: computed('value.term', 'value.priority', function() {
return get(this.value, 'priority') === TERM_PRIORITY.REQUIRED ? get(this.value, 'term') : get(this.value, 'term.podAffinityTerm')
}),
topologyKey: computed('value.topologyKey', function(){
return get(this.value, 'topologyKey') || ''
}),
namespaces: computed('term.namespaces', 'namespaceMode', {
weight: computed('value.term', 'value.priority', {
get(){
const namespaces = get(this.term, 'namespaces') || [];
return get(this.value, 'priority') === TERM_PRIORITY.REQUIRED ? null : get(this.value, 'term.weight')
},
set(key, val){
if (val || val === 0) {
try {
const toInt = parseInt(val, 10)
set(this.value.term, 'weight', toInt)
} catch (e){
// TODO nb handle bad weight value better
console.error(e)
}
} else if (this.value?.term?.weight){
delete this.value.term.weight
this.notifyPropertyChange('value')
}
return val
}
}),
namespaces: computed('podAffintyTerm.namespaces', 'namespaceMode', {
get(){
const namespaces = get(this.podAffintyTerm, 'namespaces') || [];
return namespaces.join(', ')
},
@ -117,9 +136,9 @@ export default Component.extend({
// a,b,c or a, b, c
const parsed = val.split(/,\s*/g).map((ns) => ns.trim()).filter((ns) => !!ns)
set(this.term, 'namespaces', parsed)
} else if (this.term.namespaces){
delete this.term.namespaces
set(this.podAffintyTerm, 'namespaces', parsed)
} else if (this.podAffintyTerm.namespaces){
delete this.podAffintyTerm.namespaces
this.notifyPropertyChange('value')
}
@ -127,15 +146,15 @@ export default Component.extend({
}
}),
namespaceSelector: computed('namespaceMode', 'term.namespaceSelector', {
namespaceSelector: computed('namespaceMode', 'podAffintyTerm.namespaceSelector', {
get(){
return this.term.namespaceSelector || {}
return this.podAffintyTerm.namespaceSelector || {}
},
set(key, val){
if (val){
set(this.term, 'namespaceSelector', val)
} else if (this.term.namespaceSelector){
delete this.term.namespaceSelector
set(this.podAffintyTerm, 'namespaceSelector', val)
} else if (this.podAffintyTerm.namespaceSelector){
delete this.podAffintyTerm.namespaceSelector
this.notifyPropertyChange('value')
}
@ -178,19 +197,17 @@ export default Component.extend({
// null or empty namespaces and null selector = 'this pod's namespace'
// null or empty namespaces and empty object selector = 'all namespaces'
namespaceMode: computed('term.namespaceSelector', 'term.namespaces.length', {
namespaceMode: computed('podAffintyTerm.namespaceSelector', 'podAffintyTerm.namespaces.length', {
get(){
if (this.term.namespaceSelector){
if (this.podAffintyTerm.namespaceSelector){
return namespaceModes.ALL
} else if (this.term.namespaces){
} else if (this.podAffintyTerm.namespaces){
return namespaceModes.IN_LIST
}
return namespaceModes.THIS_POD
},
set(key, val){
// const namespaceMode = get(this, 'namespaceMode');
switch (val){
case namespaceModes.ALL:
set(this, 'namespaceSelector', {})
@ -208,7 +225,6 @@ export default Component.extend({
return val
}
})
}),
})

View File

@ -36,7 +36,7 @@
</div>
<div class="row">
<div class="col span-12">
<div class="radio">
<div class="radio">
<label>
{{radio-button
selection=namespaceMode
@ -73,19 +73,37 @@
{{/input-or-display}}
{{/if}}
</div>
<div class="col span-12">
</div>
<div class="row">
<div class="col span-6">
<label class="acc-label">{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.topologyKey.label"}}</label>
{{#input-or-display
editable=editing
value=toplogyKey
value=podAffintyTerm.topologyKey
}}
{{input
type="text"
class="form-control input-sm"
value=topologyKey
value=podAffintyTerm.topologyKey
placeholder=(t "clusterNew.agentConfig.overrideAffinity.podAffinity.topologyKey.placeholder")
}}
{{/input-or-display}}
</div>
{{#if (eq value.priority TERM_PRIORITY.PREFERRED)}}
<div class="col span-6">
<label class="acc-label">{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.weight.label"}}</label>
{{#input-or-display
editable=editing
value=weight
}}
{{input
type="number"
class="form-control input-sm"
value=weight
placeholder=(t "clusterNew.agentConfig.overrideAffinity.podAffinity.weight.placeholder")
}}
{{/input-or-display}}
</div>
{{/if}}
</div>
</div>

View File

@ -3374,9 +3374,13 @@ clusterNew:
all: All Namespaces
thisPod: This pod's namespace
inList: 'Namespaces in this list:'
weight:
placeholder: e.g. 1
label: Weight
priority: Priority
nodeAffinity:
title: Node Affinity
addTerm: Add Node Selector
aliyunack:
shortLabel: Alibaba ACK
amazoneks: