pod affinity/antiaffinity preferred/required dropdowns

This commit is contained in:
Nancy Butler 2023-04-17 07:18:02 -07:00
parent c8ff871e0a
commit 3da4135901
8 changed files with 421 additions and 55 deletions

View File

@ -86,15 +86,110 @@ export default Component.extend({
value: null,
mode: 'new',
podAffinity: computed('value.podAffinity', function(){
return get(this.value, 'podAffinity') || {}
actions: {
affinityChanged(key, val){
// TODO nb update affinity
console.log('affinity ', key, ' changed to: ', val)
}
},
podAffinity: computed('value.podAffinity.{preferredDuringSchedulingIgnoredDuringExecution,requiredDuringSchedulingIgnoredDuringExecution}', {
get(){
if (!this.value?.podAffinity){
set(this.value, 'podAffinity', {})
}
this.initPreferredRequired(this.value.podAffinity)
return get(this.value, 'podAffinity')
},
set(key, val){
// TODO nb move withoutId to own function
const withoutId = {
preferredDuringSchedulingIgnoredDuringExecution: (val.preferredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
return term
}),
requiredDuringSchedulingIgnoredDuringExecution: (val.requiredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
return term
})
}
set(this.value, 'podAffinity', withoutId)
this.notifyPropertyChange('value')
return val
}
}),
podAntiAffinity: computed('value.podAntiAffinity', function(){
return get(this.value, 'podAntiAffinity') || {}
podAntiAffinity: computed('value.podAntiAffinity.{preferredDuringSchedulingIgnoredDuringExecution,requiredDuringSchedulingIgnoredDuringExecution}', {
get(){
if (!this.value?.podAntiAffinity){
set(this.value, 'podAntiAffinity', {})
}
this.initPreferredRequired(this.value.podAntiAffinity)
return get(this.value, 'podAntiAffinity')
},
set(key, val){
const withoutId = {
preferredDuringSchedulingIgnoredDuringExecution: (val.preferredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
return term
}),
requiredDuringSchedulingIgnoredDuringExecution: (val.requiredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
return term
})
}
set(this.value, 'podAntiAffinity', withoutId)
this.notifyPropertyChange('value')
return val
}
}),
nodeAffinity: computed('value.nodeAffinity', function(){
return get(this.value, 'nodeAffinity') || {}
nodeAffinity: computed('value.nodeAffinity.{preferredDuringSchedulingIgnoredDuringExecution,requiredDuringSchedulingIgnoredDuringExecution}', {
get(){
if (!this.value?.nodeAffinity){
set(this.value, 'nodeAffinity', {})
}
this.initPreferredRequired(this.value.nodeAffinity)
return get(this.value, 'nodeAffinity')
},
set(key, val){
const withoutId = {
preferredDuringSchedulingIgnoredDuringExecution: (val.preferredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
return term
}),
requiredDuringSchedulingIgnoredDuringExecution: (val.requiredDuringSchedulingIgnoredDuringExecution || []).map((term) => {
delete term._id;
return term
})
}
set(this.value, 'nodeAffinity', withoutId)
this.notifyPropertyChange('value')
return val
}
}),
initPreferredRequired: (val) => {
if (!val.preferredDuringSchedulingIgnoredDuringExecution){
set(val, 'preferredDuringSchedulingIgnoredDuringExecution', [])
}
if (!val.requiredDuringSchedulingIgnoredDuringExecution){
set(val, 'requiredDuringSchedulingIgnoredDuringExecution', [])
}
}
})

View File

@ -9,13 +9,9 @@
<FormPodAffinityK8s
@mode={{mode}}
@value={{podAffinity}}
/>
</div>
<div class="row">
<FormPodAffinityK8s
@anti={{true}}
@mode={{mode}}
@value={{podAntiAffinity}}
@podAffinity={{podAffinity}}
@podAntiAffinity={{podAntiAffinity}}
@update={{action "affinityChanged"}}
/>
</div>
</div>

View File

@ -106,7 +106,8 @@ export default Component.extend({
return get(this, 'agentConfig.overrideAffinity') || {}
},
set(key, val){
// TODO nb update agentConfig
set(this.agentConfig, 'overrideAffinity', val)
return val
}
}),

View File

@ -6,6 +6,7 @@ import {
set,
observer
} from '@ember/object';
import { randomStr } from '../../utils/util';
/** podAffinity: {
* preferredDuringSchedulingIgnoredDuringExecution:
@ -16,13 +17,57 @@ import {
* requiredDuringSchedulingIgnoredDuringExecution:
* - podaffinityterm
* }
* podAntiAffinity: same shape
*/
export const TERM_PRIORITY = {
PREFERRED: 'preferred',
REQUIRED: 'required'
}
export default Component.extend({
layout,
value: null,
mode: 'new',
anti: false,
TERM_PRIORITY,
podAffinity: null,
podAntiAffinity: null,
value: null,
mode: 'new',
anti: false,
allTerms: [],
/**
* this component renders one list for required & preferred arrays of terms in affinity & antiAffinity
* each podaffinitytermk8s component can change between affinity and antiaffinity and 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
* anti: bool
* term:preferred or required term
* }
*
*/
init(){
this._super(...arguments);
const allTerms = []
const addTerms = function(terms = [], priority, isAnti){
terms.forEach((term) => {
allTerms.push({
priority,
anti: isAnti,
term
})
})
}
addTerms(this.podAffinity?.preferredDuringSchedulingIgnoredDuringExecution, TERM_PRIORITY.PREFERRED, false)
addTerms(this.podAffinity?.requiredDuringSchedulingIgnoredDuringExecution, TERM_PRIORITY.REQUIRED, false)
addTerms(this.podAntiAffinity?.preferredDuringSchedulingIgnoredDuringExecution, TERM_PRIORITY.PREFERRED, true)
addTerms(this.podAntiAffinity?.requiredDuringSchedulingIgnoredDuringExecution, TERM_PRIORITY.REQUIRED, true)
set(this, 'allTerms', allTerms)
},
actions: {
addRequired(){
@ -32,8 +77,137 @@ export default Component.extend({
this.preferredDuringSchedulingIgnoredDuringExecution.pushObject({})
},
removeTerm(term, key){
get(this, key).removeObject(term)
addTerm(){
const neu = {
priority: TERM_PRIORITY.REQUIRED,
anti: false,
term: { _id: randomStr() }
}
this.allTerms.push(neu)
get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution').addObject(neu.term)
this.notifyPropertyChange('allTerms')
this.notifyPropertyChange('podAffinity')
// TODO nb update relevent spec array
},
removeTerm(term){
// TODO nb update relevent spec array
get(this, 'allTerms').removeObject(term)
if (term.anti){
if (term.priority === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
removeFrom.removeObject(term.term)
} else {
const removeFrom = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
removeFrom.removeObject(term.term)
}
this.notifyPropertyChange('podAntiAffinity')
} else {
if (term.priority === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
removeFrom.removeObject(term.term)
} else {
const removeFrom = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
removeFrom.removeObject(term.term)
}
this.notifyPropertyChange('podAffinity')
}
},
affinityChanged(){
// TODO nb this.update()
// needed?
},
typeChanged(term, old, neu){
if (term.anti){
if (old === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
} else {
const removeFrom = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
}
this.notifyPropertyChange('podAntiAffinity')
} else {
if (old === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
} else {
const removeFrom = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
}
this.notifyPropertyChange('podAffinity')
}
},
antiChanged(term, old, neu){
if (old){
if (term.priority === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
} else {
const removeFrom = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
}
} else {
if (term.priority === TERM_PRIORITY.REQUIRED){
const removeFrom = get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
} else {
const removeFrom = get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
const addTo = get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution')
// removeFrom.removeObject(term.term)
set(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', this.removeFromSpec(term.term, removeFrom))
addTo.pushObject(term.term)
}
}
this.notifyPropertyChange('podAffinity')
this.notifyPropertyChange('podAntiAffinity')
}
},
@ -43,14 +217,38 @@ export default Component.extend({
return mode === 'new' || mode === 'edit'
}),
requiredDuringSchedulingIgnoredDuringExecution: computed('value.requiredDuringSchedulingIgnoredDuringExecution', function(){
return get(this.value, 'requiredDuringSchedulingIgnoredDuringExecution') || []
}),
removeFromSpec: (term, array) => {
return array.filter((each) => each._id !== term._id)
},
preferredDuringSchedulingIgnoredDuringExecution: computed('value.preferredDuringSchedulingIngnoredDuringExecution', function(){
return get(this.value, 'preferredDuringSchedulingIgnoredDuringExecution') || []
}),
// affinityRequiredDuringSchedulingIgnoredDuringExecution: computed('podAffinity.requiredDuringSchedulingIgnoredDuringExecution', {
// get(){
// return get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution') || []
// },
// set(key, val){
// debugger
// if (get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution')){
// get(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution').addObject(val)
// } else {
// set(this.podAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', [val])
// }
// this.notifyPropertyChange('podAffinity')
// return val
// }
// }),
// affinityPreferredDuringSchedulingIgnoredDuringExecution: computed('podAffinity.preferredDuringSchedulingIngnoredDuringExecution', function(){
// return get(this.podAffinity, 'preferredDuringSchedulingIgnoredDuringExecution') || []
// }),
// antiAffinityRequiredDuringSchedulingIgnoredDuringExecution: computed('podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution', function(){
// return get(this.podAntiAffinity, 'requiredDuringSchedulingIgnoredDuringExecution') || []
// }),
// antiAffinityPreferredDuringSchedulingIgnoredDuringExecution: computed('podAntiAffinity.preferredDuringSchedulingIngnoredDuringExecution', function(){
// return get(this.podAntiAffinity, 'preferredDuringSchedulingIgnoredDuringExecution') || []
// }),
})

View File

@ -9,41 +9,23 @@
</label>
</div>
<div class="row">
<div class="col span-6">
<div class="col span-12">
<div>
{{#each requiredDuringSchedulingIgnoredDuringExecution as |requiredTerm|}}
{{#each allTerms as |term|}}
<FormPodAffinityTermK8s
@value={{requiredTerm}}
@value={{term}}
@mode={{mode}}
@remove={{action "removeTerm" requiredTerm "requiredDuringSchedulingIgnoredDuringExecution"}}
@remove={{action "removeTerm" term}}
@typeChanged={{action "typeChanged" term}}
@antiChanged={{action "antiChanged" term}}
/>
{{/each}}
</div>
{{#if editing}}
<button class="btn bg-link icon-btn" type="button" {{action "addRequired"}}>
<button class="btn bg-link icon-btn" type="button" {{action "addTerm"}}>
<i class="icon icon-plus text-small"/>
<span>
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.addRequired"}}
</span>
</button>
{{/if}}
</div>
<div class="col span-6">
<div>
{{#each preferredDuringSchedulingIgnoredDuringExecution as |preferredTerm|}}
<FormPodAffinityTermK8s
@value={{preferredTerm}}
@mode={{mode}}
@preferred={{true}}
@remove={{action "removeTerm" prefferedTerm "preferredDuringSchedulingIgnoredDuringExecution"}}
/>
{{/each}}
</div>
{{#if editing}}
<button class="btn bg-link icon-btn" type="button" {{action "addPreferred"}}>
<i class="icon icon-plus text-small"/>
<span>
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.addPreferred"}}
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.addTerm"}}
</span>
</button>
{{/if}}

View File

@ -6,6 +6,7 @@ import {
set,
observer
} from '@ember/object';
import { TERM_PRIORITY } from '../form-pod-affinity-k8s/component';
/** if preferred:
* - {
@ -46,10 +47,38 @@ export default Component.extend({
layout,
namespaceModes,
/**
* {
* priority: preferred or required
* anti: bool - podAffinity or podAntiAffinity
* term: podaffinityterm OR
* {
* weight,
* podAffintyTerm
* }
* }
*/
value: null,
mode: 'new',
perferred: false,
remove: null,
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',
label: 'clusterNew.agentConfig.overrideAffinity.podAffinity.typeOptions.antiAffinity'
}, {
value: 'false',
label: 'clusterNew.agentConfig.overrideAffinity.podAffinity.typeOptions.affinity'
}],
actions: {
removeTerm(){
@ -95,7 +124,6 @@ export default Component.extend({
return namespaces.join(', ')
},
set(key, val){
// TODO nb delete if not set
if (val || val === ''){
// a,b,c or a, b, c
const parsed = val.split(/,\s*/g).map((ns) => ns.trim()).filter((ns) => !!ns)
@ -126,6 +154,39 @@ export default Component.extend({
}
}),
priority: computed('priorityOptions', 'value.priority', {
get(){
return get(this.value, 'priority')
},
set(key, val){
const old = get(this.value, 'priority')
set(this.value, 'priority', val)
this.typeChanged(old, val)
return val
},
}),
anti: computed('affinityOptions', 'value.anti', {
get(){
const isAnti = get(this.value, 'anti')
return isAnti ? 'true' : 'false'
},
set(key, val){
const old = get(this, 'anti') === 'true'
const neu = val === 'true'
set(this.value, 'anti', neu)
this.antiChanged(old, neu)
return val
},
}),
namespaceMode: namespaceModes.ALL,

View File

@ -6,6 +6,34 @@
</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 class="col span-6">
{{!-- <label class="acc-label">
{{t "clusterNew.agentConfig.overrideAffinity.podAffinity.priority"}}
</label> --}}
<NewSelect
@class="form-control"
@value={{anti}}
@optionLabelPath="label"
@optionValuePath="value"
@localizedLabel={{true}}
@content={{affinityOptions}}
/>
</div>
</div>
<div class="row">
<div class="col span-12">
<div class="radio">

View File

@ -3360,16 +3360,21 @@ clusterNew:
title: Pod AntiAffinity
podAffinity:
title: Pod Affinity
addRequired: Add Required Pod Affinity Term
addPreferred: Add Preferred Pod Affinity Term
addTerm: Add Pod Affinity Term
topologyKey:
label: Topology Key
placeholder: e.g. failure-domain.beta.kubernetes.io/zone
typeOptions:
affinity: Affinity
antiAffinity: AntiAffinity
preferred: Preferred
required: Required
namespaces:
radioOptions:
all: All Namespaces
thisPod: This pod's namespace
inList: 'Namespaces in this list:'
priority: Priority
nodeAffinity:
title: Node Affinity
aliyunack: