Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions shell/edit/networking.k8s.io.ingress/DefaultBackend.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export default {
const backend = get(this.value.spec, this.value.defaultBackendPath);

this.serviceName = get(backend, this.value.serviceNamePath) || '';
this.servicePort = get(backend, this.value.servicePortPath) || '';
this.servicePort = get(backend, this.value.servicePortPath) ||
get(backend, this.value.servicePortNamePath) ||
'';
},
computed: {
isView() {
Expand Down Expand Up @@ -75,10 +77,14 @@ export default {
},
methods: {
update() {
const backend = get(this.value.spec, this.value.defaultBackendPath) || {};
// Fresh object so the old port path (name vs number) doesn't linger.
const backend = {};
const parsed = Number.parseInt(this.servicePort);
const servicePort = Number.isNaN(parsed) ? this.servicePort : parsed;
const portPath = typeof servicePort === 'number' ? this.value.servicePortPath : this.value.servicePortNamePath;

set(backend, this.value.serviceNamePath, this.serviceName);
set(backend, this.value.servicePortPath, this.servicePort);
set(backend, portPath, servicePort);
set(this.value.spec, this.value.defaultBackendPath, backend);

this.$emit('update:value', this.value);
Expand Down Expand Up @@ -119,10 +125,12 @@ export default {
class="col span-3"
:style="{'margin-right': '0px'}"
>
<!-- :required drives the asterisk; portRequired doesn't have .name === 'required' -->
<LabeledInput
v-if="portOptions.length === 0 || isView"
v-model:value.number="servicePort"
v-model:value="servicePort"
:mode="mode"
:required="true"
:label="t('ingress.defaultBackend.port.label')"
:placeholder="t('ingress.defaultBackend.port.placeholder')"
:rules="rules.port"
Expand All @@ -132,6 +140,7 @@ export default {
v-else
v-model:value="servicePort"
:mode="mode"
:required="true"
:options="portOptions"
:label="t('ingress.defaultBackend.port.label')"
:placeholder="t('ingress.defaultBackend.port.placeholder')"
Expand Down
12 changes: 8 additions & 4 deletions shell/edit/networking.k8s.io.ingress/RulePath.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,24 @@ export default {
set(this.value, 'path', this.value.path || '');
set(this.value, 'pathType', this.value.pathType || this.pathTypes[0]);
set(this.value.backend, this.ingress.serviceNamePath, get(this.value.backend, this.ingress.serviceNamePath) || '');
set(this.value.backend, this.ingress.servicePortPath, get(this.value.backend, this.ingress.servicePortPath) || '');

this.serviceName = get(this.value.backend, this.ingress.serviceNamePath);
this.servicePort = get(this.value.backend, this.ingress.servicePortPath);
this.servicePort = get(this.value.backend, this.ingress.servicePortPath) ||
get(this.value.backend, this.ingress.servicePortNamePath) ||
'';
},
methods: {
update() {
const servicePort = Number.parseInt(this.servicePort) || this.servicePort;
const parsed = Number.parseInt(this.servicePort);
const servicePort = Number.isNaN(parsed) ? this.servicePort : parsed;
const serviceName = this.serviceName.label || this.serviceName;
const out = {
id: this.value.id, backend: {}, path: this.path, pathType: this.pathType
};

set(out.backend, this.ingress.servicePortPath, servicePort);
const portPath = typeof servicePort === 'number' ? this.ingress.servicePortPath : this.ingress.servicePortNamePath;

set(out.backend, portPath, servicePort);
set(out.backend, this.ingress.serviceNamePath, serviceName);

this.$emit('update:value', out);
Expand Down
95 changes: 75 additions & 20 deletions shell/edit/networking.k8s.io.ingress/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,21 @@ export default {
path: 'spec.rules.http.paths.path', rules: ['absolutePath'], translationKey: 'ingress.rules.path.label'
},
{
path: 'spec.rules.http.paths.backend.service.port.number', rules: ['required'], translationKey: 'ingress.rules.port.label'
path: 'spec.rules.http.paths.backend.service.port', rules: ['portRequired', 'portRange'], translationKey: 'ingress.rules.port.label'
},
{
path: 'spec.rules.http.paths.backend.service.name', rules: ['required'], translationKey: 'ingress.rules.target.label'
},
{ path: 'spec', rules: ['backEndOrRules'] },
{
path: 'spec.defaultBackend.service.name', rules: ['required'], translationKey: 'ingress.defaultBackend.targetService.label'
path: 'spec.defaultBackend.service.name', rules: ['defaultBackendNameRequired'], translationKey: 'ingress.defaultBackend.targetService.label'
},
{
path: 'spec.defaultBackend.service.port.number', rules: ['required', 'requiredInt', 'portNumber'], translationKey: 'ingress.defaultBackend.port.label'
path: 'spec.defaultBackend.service.port', rules: ['defaultBackendPortRequired', 'portRange'], translationKey: 'ingress.defaultBackend.port.label'
},
{ path: 'spec.tls.hosts', rules: ['required', 'wildcardHostname'] }
],
fvReportedValidationPaths: ['spec.rules.http.paths.backend.service.port.number', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.name']
fvReportedValidationPaths: ['spec.rules.http.paths.backend.service.port', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.name']
};
},

Expand All @@ -125,35 +125,89 @@ export default {
}
};

return { backEndOrRules };
const portLabel = this.t('ingress.rules.port.label');

// Built-in `required` won't work: it passes for empty objects like {} or { name: '' }.
const portRequired = (port) => {
if (typeof port === 'string' || typeof port === 'number') {
if (!port) {
return this.t('validation.required', { key: portLabel });
}
} else if (!port || (!port.number && !port.name)) {
return this.t('validation.required', { key: portLabel });
}
};

const portRange = (port) => {
let num;

if (typeof port === 'number') {
num = port;
} else if (typeof port === 'string') {
num = Number.parseInt(port);
} else if (port?.number) {
num = port.number;
}

if (num !== undefined && !Number.isNaN(num) && (num < 1 || num > 65535)) {
return this.t('validation.number.between', {
key: portLabel, min: '1', max: '65535'
});
}
};

const hasDefaultBackendService = () => {
const backend = get(this.value?.spec, this.value.defaultBackendPath);

return !!get(backend, this.value.serviceNamePath);
};

const nameLabel = this.t('ingress.defaultBackend.targetService.label');

// Only enforce required on the default backend when a service is selected.
// Selecting "None" means the user wants to remove the backend; willSave() handles cleanup.
const defaultBackendNameRequired = (name) => {
if (hasDefaultBackendService() && !name) {
return this.t('validation.required', { key: nameLabel });
}
};

const defaultBackendPortRequired = (port) => {
if (!hasDefaultBackendService()) {
return;
}

return portRequired(port);
};

return {
backEndOrRules,
portRequired,
portRange,
defaultBackendNameRequired,
defaultBackendPortRequired,
};
},
tabErrors() {
return {
rules: this.fvGetPathErrors(['spec.rules.host', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.port.number', 'spec.rules.http.paths.backend.service.name'])?.length > 0,
defaultBackend: this.fvGetPathErrors(['spec.defaultBackend.service.name', 'spec.defaultBackend.service.port.number'])?.length > 0
rules: this.fvGetPathErrors(['spec.rules.host', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.port', 'spec.rules.http.paths.backend.service.name'])?.length > 0,
defaultBackend: this.fvGetPathErrors(['spec.defaultBackend.service.name', 'spec.defaultBackend.service.port'])?.length > 0
};
},
rulesPathRules() {
return {
requestHost: this.fvGetAndReportPathRules('spec.rules.host'),
path: this.fvGetAndReportPathRules('spec.rules.http.paths.path'),
port: this.fvGetAndReportPathRules('spec.rules.http.paths.backend.service.port.number'),
port: this.fvGetAndReportPathRules('spec.rules.http.paths.backend.service.port'),
target: this.fvGetAndReportPathRules('spec.rules.http.paths.backend.service.name'),

};
},
defaultBackendPathRules() {
const rulesExist = (this.value?.spec?.rules || []).length > 0;
const defaultBackendExist = !!this.value?.spec?.defaultBackend?.service;

if (!rulesExist || defaultBackendExist) {
return {
name: this.fvGetAndReportPathRules('spec.defaultBackend.service.name'),
port: this.fvGetAndReportPathRules('spec.defaultBackend.service.port.number'),
};
}

return { name: [], port: [] };
return {
name: this.fvGetAndReportPathRules('spec.defaultBackend.service.name'),
port: this.fvGetAndReportPathRules('spec.defaultBackend.service.port'),
};
},
serviceTargets() {
return this.ingressHelper.findAndMapServiceTargets(this.services);
Expand Down Expand Up @@ -188,7 +242,8 @@ export default {
willSave() {
const backend = get(this.value.spec, this.value.defaultBackendPath);
const serviceName = get(backend, this.value.serviceNamePath);
const servicePort = get(backend, this.value.servicePortPath);
const servicePort = get(backend, this.value.servicePortPath) ||
get(backend, this.value.servicePortNamePath);

if (backend && (!serviceName || !servicePort)) {
const path = this.value.defaultBackendPath;
Expand Down
10 changes: 9 additions & 1 deletion shell/models/networking.k8s.io.ingress.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default class Ingress extends SteveModel {
serviceTargetTo: this.targetTo(workloads, serviceName),
certs: this.certLinks(rule, certificates),
targetLink: this.targetLink(workloads, serviceName),
port: get(path?.backend, this.servicePortPath)
port: get(path?.backend, this.servicePortPath) || get(path?.backend, this.servicePortNamePath)
};
}

Expand Down Expand Up @@ -184,6 +184,14 @@ export default class Ingress extends SteveModel {
return this.useNestedBackendField ? nestedPath : flatPath;
}

get servicePortNamePath() {
const nestedPath = 'service.port.name';
// Flat API has a single `servicePort` field for both name and number.
const flatPath = 'servicePort';

return this.useNestedBackendField ? nestedPath : flatPath;
}

get defaultBackendPath() {
const defaultBackend = this.$rootGetters['cluster/pathExistsInSchema'](this.type, 'spec.defaultBackend');

Expand Down
Loading
Loading