mirror of https://github.com/portainer/portainer
bug(ingress): fix ingress class disallowed to not found issue EE-4311 (#7731)
parent
66fd039933
commit
83a1ce9d2a
|
@ -51,41 +51,29 @@ func (handler *Handler) getKubernetesIngressControllers(w http.ResponseWriter, r
|
||||||
|
|
||||||
controllers := cli.GetIngressControllers()
|
controllers := cli.GetIngressControllers()
|
||||||
existingClasses := endpoint.Kubernetes.Configuration.IngressClasses
|
existingClasses := endpoint.Kubernetes.Configuration.IngressClasses
|
||||||
|
var updatedClasses []portainer.KubernetesIngressClassConfig
|
||||||
for i := range controllers {
|
for i := range controllers {
|
||||||
controllers[i].Availability = true
|
controllers[i].Availability = true
|
||||||
controllers[i].New = true
|
controllers[i].New = true
|
||||||
|
|
||||||
// Check if the controller is blocked globally.
|
var updatedClass portainer.KubernetesIngressClassConfig
|
||||||
for _, a := range existingClasses {
|
updatedClass.Name = controllers[i].ClassName
|
||||||
controllers[i].New = false
|
updatedClass.Type = controllers[i].Type
|
||||||
if controllers[i].ClassName != a.Name {
|
|
||||||
|
// Check if the controller is already known.
|
||||||
|
for _, existingClass := range existingClasses {
|
||||||
|
if controllers[i].ClassName != existingClass.Name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
controllers[i].New = false
|
controllers[i].New = false
|
||||||
|
controllers[i].Availability = !existingClass.GloballyBlocked
|
||||||
// Skip over non-global blocks.
|
updatedClass.GloballyBlocked = existingClass.GloballyBlocked
|
||||||
if len(a.BlockedNamespaces) > 0 {
|
updatedClass.BlockedNamespaces = existingClass.BlockedNamespaces
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if controllers[i].ClassName == a.Name {
|
|
||||||
controllers[i].Availability = !a.GloballyBlocked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
updatedClasses = append(updatedClasses, updatedClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the database to match the list of found + modified controllers.
|
endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses
|
||||||
// This includes pruning out controllers which no longer exist.
|
|
||||||
var newClasses []portainer.KubernetesIngressClassConfig
|
|
||||||
for _, controller := range controllers {
|
|
||||||
var class portainer.KubernetesIngressClassConfig
|
|
||||||
class.Name = controller.ClassName
|
|
||||||
class.Type = controller.Type
|
|
||||||
class.GloballyBlocked = !controller.Availability
|
|
||||||
class.BlockedNamespaces = []string{}
|
|
||||||
newClasses = append(newClasses, class)
|
|
||||||
}
|
|
||||||
endpoint.Kubernetes.Configuration.IngressClasses = newClasses
|
|
||||||
err = handler.DataStore.Endpoint().UpdateEndpoint(
|
err = handler.DataStore.Endpoint().UpdateEndpoint(
|
||||||
portainer.EndpointID(endpointID),
|
portainer.EndpointID(endpointID),
|
||||||
endpoint,
|
endpoint,
|
||||||
|
@ -143,7 +131,9 @@ func (handler *Handler) getKubernetesIngressControllersByNamespace(w http.Respon
|
||||||
|
|
||||||
cli := handler.KubernetesClient
|
cli := handler.KubernetesClient
|
||||||
currentControllers := cli.GetIngressControllers()
|
currentControllers := cli.GetIngressControllers()
|
||||||
existingClasses := endpoint.Kubernetes.Configuration.IngressClasses
|
kubernetesConfig := endpoint.Kubernetes.Configuration
|
||||||
|
existingClasses := kubernetesConfig.IngressClasses
|
||||||
|
ingressAvailabilityPerNamespace := kubernetesConfig.IngressAvailabilityPerNamespace
|
||||||
var updatedClasses []portainer.KubernetesIngressClassConfig
|
var updatedClasses []portainer.KubernetesIngressClassConfig
|
||||||
var controllers models.K8sIngressControllers
|
var controllers models.K8sIngressControllers
|
||||||
for i := range currentControllers {
|
for i := range currentControllers {
|
||||||
|
@ -167,10 +157,12 @@ func (handler *Handler) getKubernetesIngressControllersByNamespace(w http.Respon
|
||||||
|
|
||||||
globallyblocked = existingClass.GloballyBlocked
|
globallyblocked = existingClass.GloballyBlocked
|
||||||
|
|
||||||
// Check if the current namespace is blocked.
|
// Check if the current namespace is blocked if ingressAvailabilityPerNamespace is set to true
|
||||||
for _, ns := range existingClass.BlockedNamespaces {
|
if ingressAvailabilityPerNamespace {
|
||||||
if namespace == ns {
|
for _, ns := range existingClass.BlockedNamespaces {
|
||||||
currentControllers[i].Availability = false
|
if namespace == ns {
|
||||||
|
currentControllers[i].Availability = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +189,7 @@ func (handler *Handler) getKubernetesIngressControllersByNamespace(w http.Respon
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) updateKubernetesIngressControllers(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
func (handler *Handler) updateKubernetesIngressControllers(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
|
||||||
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httperror.BadRequest(
|
return httperror.BadRequest(
|
||||||
|
@ -237,38 +230,38 @@ func (handler *Handler) updateKubernetesIngressControllers(w http.ResponseWriter
|
||||||
|
|
||||||
existingClasses := endpoint.Kubernetes.Configuration.IngressClasses
|
existingClasses := endpoint.Kubernetes.Configuration.IngressClasses
|
||||||
controllers := cli.GetIngressControllers()
|
controllers := cli.GetIngressControllers()
|
||||||
|
var updatedClasses []portainer.KubernetesIngressClassConfig
|
||||||
for i := range controllers {
|
for i := range controllers {
|
||||||
// Set existing class data. So that we don't accidentally overwrite it
|
controllers[i].Availability = true
|
||||||
// with blank data that isn't in the payload.
|
controllers[i].New = true
|
||||||
for ii := range existingClasses {
|
|
||||||
if controllers[i].ClassName == existingClasses[ii].Name {
|
var updatedClass portainer.KubernetesIngressClassConfig
|
||||||
controllers[i].Availability = !existingClasses[ii].GloballyBlocked
|
updatedClass.Name = controllers[i].ClassName
|
||||||
|
updatedClass.Type = controllers[i].Type
|
||||||
|
|
||||||
|
// Check if the controller is already known.
|
||||||
|
for _, existingClass := range existingClasses {
|
||||||
|
if controllers[i].ClassName != existingClass.Name {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
controllers[i].New = false
|
||||||
|
controllers[i].Availability = !existingClass.GloballyBlocked
|
||||||
|
updatedClass.GloballyBlocked = existingClass.GloballyBlocked
|
||||||
|
updatedClass.BlockedNamespaces = existingClass.BlockedNamespaces
|
||||||
}
|
}
|
||||||
|
updatedClasses = append(updatedClasses, updatedClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range payload {
|
for _, p := range payload {
|
||||||
for i := range controllers {
|
for i := range controllers {
|
||||||
// Now set new payload data
|
// Now set new payload data
|
||||||
if p.ClassName == controllers[i].ClassName {
|
if updatedClasses[i].Name == p.ClassName {
|
||||||
controllers[i].Availability = p.Availability
|
updatedClasses[i].GloballyBlocked = !p.Availability
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the database to match the list of found + modified controllers.
|
endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses
|
||||||
// This includes pruning out controllers which no longer exist.
|
|
||||||
var newClasses []portainer.KubernetesIngressClassConfig
|
|
||||||
for _, controller := range controllers {
|
|
||||||
var class portainer.KubernetesIngressClassConfig
|
|
||||||
class.Name = controller.ClassName
|
|
||||||
class.Type = controller.Type
|
|
||||||
class.GloballyBlocked = !controller.Availability
|
|
||||||
class.BlockedNamespaces = []string{}
|
|
||||||
newClasses = append(newClasses, class)
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoint.Kubernetes.Configuration.IngressClasses = newClasses
|
|
||||||
err = handler.DataStore.Endpoint().UpdateEndpoint(
|
err = handler.DataStore.Endpoint().UpdateEndpoint(
|
||||||
portainer.EndpointID(endpointID),
|
portainer.EndpointID(endpointID),
|
||||||
endpoint,
|
endpoint,
|
||||||
|
@ -327,7 +320,6 @@ PayloadLoop:
|
||||||
for _, p := range payload {
|
for _, p := range payload {
|
||||||
for _, existingClass := range existingClasses {
|
for _, existingClass := range existingClasses {
|
||||||
if p.ClassName != existingClass.Name {
|
if p.ClassName != existingClass.Name {
|
||||||
updatedClasses = append(updatedClasses, existingClass)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var updatedClass portainer.KubernetesIngressClassConfig
|
var updatedClass portainer.KubernetesIngressClassConfig
|
||||||
|
@ -345,7 +337,7 @@ PayloadLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedClasses = append(updatedClasses, existingClass)
|
updatedClasses = append(updatedClasses, updatedClass)
|
||||||
continue PayloadLoop
|
continue PayloadLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +348,7 @@ PayloadLoop:
|
||||||
updatedClass.BlockedNamespaces = existingClass.BlockedNamespaces
|
updatedClass.BlockedNamespaces = existingClass.BlockedNamespaces
|
||||||
for _, ns := range updatedClass.BlockedNamespaces {
|
for _, ns := range updatedClass.BlockedNamespaces {
|
||||||
if namespace == ns {
|
if namespace == ns {
|
||||||
updatedClasses = append(updatedClasses, existingClass)
|
updatedClasses = append(updatedClasses, updatedClass)
|
||||||
continue PayloadLoop
|
continue PayloadLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,6 +360,24 @@ PayloadLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At this point it's possible we had an existing class which was globally
|
||||||
|
// blocked and thus not included in the payload. As a result it is not yet
|
||||||
|
// part of updatedClasses, but we MUST include it or we would remove the
|
||||||
|
// global block.
|
||||||
|
for _, existingClass := range existingClasses {
|
||||||
|
var found bool
|
||||||
|
|
||||||
|
for _, updatedClass := range updatedClasses {
|
||||||
|
if existingClass.Name == updatedClass.Name {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
updatedClasses = append(updatedClasses, existingClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses
|
endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses
|
||||||
err = handler.DataStore.Endpoint().UpdateEndpoint(
|
err = handler.DataStore.Endpoint().UpdateEndpoint(
|
||||||
portainer.EndpointID(endpointID),
|
portainer.EndpointID(endpointID),
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { PageHeader } from '@@/PageHeader';
|
||||||
import { Option } from '@@/form-components/Input/Select';
|
import { Option } from '@@/form-components/Input/Select';
|
||||||
import { Button } from '@@/buttons';
|
import { Button } from '@@/buttons';
|
||||||
|
|
||||||
import { Ingress } from '../types';
|
import { Ingress, IngressController } from '../types';
|
||||||
import {
|
import {
|
||||||
useCreateIngress,
|
useCreateIngress,
|
||||||
useIngresses,
|
useIngresses,
|
||||||
|
@ -66,7 +66,8 @@ export function CreateIngressView() {
|
||||||
(servicesResults.isLoading &&
|
(servicesResults.isLoading &&
|
||||||
configResults.isLoading &&
|
configResults.isLoading &&
|
||||||
namespacesResults.isLoading &&
|
namespacesResults.isLoading &&
|
||||||
ingressesResults.isLoading) ||
|
ingressesResults.isLoading &&
|
||||||
|
ingressControllersResults.isLoading) ||
|
||||||
(isEdit && !ingressRule.IngressName);
|
(isEdit && !ingressRule.IngressName);
|
||||||
|
|
||||||
const [ingressNames, ingresses, ruleCounterByNamespace, hostWithTLS] =
|
const [ingressNames, ingresses, ruleCounterByNamespace, hostWithTLS] =
|
||||||
|
@ -166,7 +167,12 @@ export function CreateIngressView() {
|
||||||
})) || []),
|
})) || []),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!existingIngressClass && ingressRule.IngressClassName) {
|
if (
|
||||||
|
(!existingIngressClass ||
|
||||||
|
(existingIngressClass && !existingIngressClass.Availability)) &&
|
||||||
|
ingressRule.IngressClassName &&
|
||||||
|
ingressControllersResults.data
|
||||||
|
) {
|
||||||
ingressClassOptions.push({
|
ingressClassOptions.push({
|
||||||
label: !ingressRule.IngressType
|
label: !ingressRule.IngressType
|
||||||
? `${ingressRule.IngressClassName} - NOT FOUND`
|
? `${ingressRule.IngressClassName} - NOT FOUND`
|
||||||
|
@ -189,7 +195,12 @@ export function CreateIngressView() {
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!!params.name && ingressesResults.data && !ingressRule.IngressName) {
|
if (
|
||||||
|
!!params.name &&
|
||||||
|
ingressesResults.data &&
|
||||||
|
!ingressRule.IngressName &&
|
||||||
|
ingressControllersResults.data
|
||||||
|
) {
|
||||||
// if it is an edit screen, prepare the rule from the ingress
|
// if it is an edit screen, prepare the rule from the ingress
|
||||||
const ing = ingressesResults.data?.find(
|
const ing = ingressesResults.data?.find(
|
||||||
(ing) => ing.Name === params.name && ing.Namespace === params.namespace
|
(ing) => ing.Name === params.name && ing.Namespace === params.namespace
|
||||||
|
@ -199,7 +210,7 @@ export function CreateIngressView() {
|
||||||
(c) => c.ClassName === ing.ClassName
|
(c) => c.ClassName === ing.ClassName
|
||||||
)?.Type;
|
)?.Type;
|
||||||
const r = prepareRuleFromIngress(ing);
|
const r = prepareRuleFromIngress(ing);
|
||||||
r.IngressType = type;
|
r.IngressType = type || r.IngressType;
|
||||||
setIngressRule(r);
|
setIngressRule(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,9 +228,10 @@ export function CreateIngressView() {
|
||||||
ingressRule,
|
ingressRule,
|
||||||
ingressNames || [],
|
ingressNames || [],
|
||||||
servicesOptions || [],
|
servicesOptions || [],
|
||||||
!!existingIngressClass
|
existingIngressClass
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
ingressRule,
|
ingressRule,
|
||||||
namespace,
|
namespace,
|
||||||
|
@ -289,7 +301,7 @@ export function CreateIngressView() {
|
||||||
ingressRule: Rule,
|
ingressRule: Rule,
|
||||||
ingressNames: string[],
|
ingressNames: string[],
|
||||||
serviceOptions: Option<string>[],
|
serviceOptions: Option<string>[],
|
||||||
existingIngressClass: boolean
|
existingIngressClass?: IngressController
|
||||||
) {
|
) {
|
||||||
const errors: Record<string, ReactNode> = {};
|
const errors: Record<string, ReactNode> = {};
|
||||||
const rule = { ...ingressRule };
|
const rule = { ...ingressRule };
|
||||||
|
@ -320,7 +332,12 @@ export function CreateIngressView() {
|
||||||
'No ingress class is currently set for this ingress - use of the Portainer UI requires one to be set.';
|
'No ingress class is currently set for this ingress - use of the Portainer UI requires one to be set.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEdit && !existingIngressClass && ingressRule.IngressClassName) {
|
if (
|
||||||
|
isEdit &&
|
||||||
|
(!existingIngressClass ||
|
||||||
|
(existingIngressClass && !existingIngressClass.Availability)) &&
|
||||||
|
ingressRule.IngressClassName
|
||||||
|
) {
|
||||||
if (!rule.IngressType) {
|
if (!rule.IngressType) {
|
||||||
errors.className =
|
errors.className =
|
||||||
'Currently set to an ingress class that cannot be found in the cluster - you must select a valid class.';
|
'Currently set to an ingress class that cannot be found in the cluster - you must select a valid class.';
|
||||||
|
|
|
@ -102,8 +102,9 @@ export function IngressForm({
|
||||||
}
|
}
|
||||||
const hasNoHostRule = rule.Hosts?.some((host) => host.NoHost);
|
const hasNoHostRule = rule.Hosts?.some((host) => host.NoHost);
|
||||||
const placeholderAnnotation =
|
const placeholderAnnotation =
|
||||||
PlaceholderAnnotations[rule.IngressType || 'other'];
|
PlaceholderAnnotations[rule.IngressType || 'other'] ||
|
||||||
const pathTypes = PathTypes[rule.IngressType || 'other'];
|
PlaceholderAnnotations.other;
|
||||||
|
const pathTypes = PathTypes[rule.IngressType || 'other'] || PathTypes.other;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget>
|
<Widget>
|
||||||
|
@ -169,7 +170,7 @@ export function IngressForm({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group" key={rule.IngressClassName}>
|
<div className="form-group" key={ingressClassOptions.toString()}>
|
||||||
<label
|
<label
|
||||||
className="control-label text-muted col-sm-3 col-lg-2 required"
|
className="control-label text-muted col-sm-3 col-lg-2 required"
|
||||||
htmlFor="ingress_class"
|
htmlFor="ingress_class"
|
||||||
|
|
Loading…
Reference in New Issue