-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[feat gw api] Implement Gateway Status + Some bug fixes for Gateway #4189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,11 @@ package gateway | |
import ( | ||
"context" | ||
"fmt" | ||
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" | ||
"github.com/go-logr/logr" | ||
"github.com/pkg/errors" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apimachinery/pkg/util/sets" | ||
"k8s.io/client-go/tools/record" | ||
|
@@ -35,6 +37,12 @@ import ( | |
"sigs.k8s.io/controller-runtime/pkg/source" | ||
gwv1 "sigs.k8s.io/gateway-api/apis/v1" | ||
gwalpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" | ||
"time" | ||
) | ||
|
||
const ( | ||
requeueMessage = "Monitoring provisioning state" | ||
statusUpdateRequeueTime = 2 * time.Minute | ||
) | ||
|
||
var _ Reconciler = &gatewayReconciler{} | ||
|
@@ -86,6 +94,7 @@ func newGatewayReconciler(controllerName string, lbType elbv2model.LoadBalancerT | |
reconcileTracker: reconcileTracker, | ||
cfgResolver: cfgResolver, | ||
routeReconciler: routeReconciler, | ||
gatewayConditionUpdater: prepareGatewayConditionUpdate, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like you are calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice find, thanks. i've replaced the direct usage with calls from the struct. |
||
} | ||
} | ||
|
||
|
@@ -107,6 +116,7 @@ type gatewayReconciler struct { | |
logger logr.Logger | ||
metricsCollector lbcmetrics.MetricCollector | ||
reconcileTracker func(namespaceName types.NamespacedName) | ||
gatewayConditionUpdater func(gw *gwv1.Gateway, targetConditionType string, newStatus metav1.ConditionStatus, reason string, message string) bool | ||
|
||
cfgResolver gatewayConfigResolver | ||
routeReconciler routeutils.RouteReconciler | ||
|
@@ -186,12 +196,23 @@ func (r *gatewayReconciler) reconcileHelper(ctx context.Context, req reconcile.R | |
mergedLbConfig, err := r.cfgResolver.getLoadBalancerConfigForGateway(ctx, r.k8sClient, gw, gwClass) | ||
|
||
if err != nil { | ||
statusErr := r.updateGatewayStatusFailure(ctx, gw, gwv1.GatewayReasonInvalid, err) | ||
if statusErr != nil { | ||
r.logger.Error(statusErr, "Unable to update gateway status on failure to retrieve attached config") | ||
} | ||
return err | ||
} | ||
|
||
allRoutes, err := r.gatewayLoader.LoadRoutesForGateway(ctx, *gw, r.routeFilter, r.routeReconciler) | ||
|
||
if err != nil { | ||
var loaderErr routeutils.LoaderError | ||
if errors.As(err, &loaderErr) { | ||
statusErr := r.updateGatewayStatusFailure(ctx, gw, loaderErr.GetGatewayReason(), loaderErr) | ||
if statusErr != nil { | ||
r.logger.Error(statusErr, "Unable to update gateway status on failure to build routes") | ||
} | ||
} | ||
return err | ||
} | ||
|
||
|
@@ -248,18 +269,14 @@ func (r *gatewayReconciler) reconcileUpdate(ctx context.Context, gw *gwv1.Gatewa | |
if err != nil { | ||
return err | ||
} | ||
lbDNS, err := lb.DNSName().Resolve(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !backendSGRequired { | ||
if err := r.backendSGProvider.Release(ctx, networking.ResourceTypeGateway, []types.NamespacedName{k8s.NamespacedName(gw)}); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if err = r.updateGatewayStatus(ctx, lbDNS, gw); err != nil { | ||
if err = r.updateGatewayStatusSuccess(ctx, lb.Status, gw); err != nil { | ||
r.eventRecorder.Event(gw, corev1.EventTypeWarning, k8s.GatewayEventReasonFailedUpdateStatus, fmt.Sprintf("Failed update status due to %v", err)) | ||
return err | ||
} | ||
|
@@ -295,28 +312,58 @@ func (r *gatewayReconciler) buildModel(ctx context.Context, gw *gwv1.Gateway, cf | |
return stack, lb, backendSGRequired, nil | ||
} | ||
|
||
func (r *gatewayReconciler) updateGatewayStatus(ctx context.Context, lbDNS string, gw *gwv1.Gateway) error { | ||
// TODO Consider LB ARN. | ||
func (r *gatewayReconciler) updateGatewayStatusSuccess(ctx context.Context, lbStatus *elbv2model.LoadBalancerStatus, gw *gwv1.Gateway) error { | ||
// LB Status should always be set, if it's not, we need to prevent NPE | ||
if lbStatus == nil { | ||
r.logger.Info("Unable to update Gateway Status due to null LB status") | ||
return nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if lbStatus == nil, the updateGatewayStatusSuccess return nil (no error).
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, i wasn't exactly sure what to do there. My thinking is that the lbstatus being null is a weird and unexpected condition. I'd prefer to silently fail (with a log message) rather than keep performing reconciles when the LBStatus is busted. |
||
} | ||
gwOld := gw.DeepCopy() | ||
|
||
var needPatch bool | ||
var requeueNeeded bool | ||
if isGatewayProgrammed(*lbStatus) { | ||
needPatch = r.gatewayConditionUpdater(gw, string(gwv1.GatewayConditionProgrammed), metav1.ConditionTrue, string(gwv1.GatewayConditionProgrammed), lbStatus.LoadBalancerARN) | ||
} else { | ||
requeueNeeded = true | ||
} | ||
|
||
// Gateway Address Status | ||
needPatch = r.gatewayConditionUpdater(gw, string(gwv1.GatewayConditionAccepted), metav1.ConditionTrue, string(gwv1.GatewayConditionAccepted), "") || needPatch | ||
if len(gw.Status.Addresses) != 1 || | ||
gw.Status.Addresses[0].Value != "" || | ||
gw.Status.Addresses[0].Value != lbDNS { | ||
gwOld := gw.DeepCopy() | ||
gw.Status.Addresses[0].Value != lbStatus.DNSName { | ||
ipAddressType := gwv1.HostnameAddressType | ||
gw.Status.Addresses = []gwv1.GatewayStatusAddress{ | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to confirm. Is this correct ? `gw.Status.Addresses[0].Value != "" The second condition will always be true if the address value is not empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't follow this comment, sorry. Are you saying that if the address is not empty, then it must equal the DNS name of the LB? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I may need to understand the intent here better. I was thinking Was your intent here to update the address only when necessary. More specifically, only when either:
Else, simply remove
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦 wow -- thanks for catching that and the explanation. |
||
Type: &ipAddressType, | ||
Value: lbDNS, | ||
Value: lbStatus.DNSName, | ||
}, | ||
} | ||
needPatch = true | ||
} | ||
|
||
if needPatch { | ||
if err := r.k8sClient.Status().Patch(ctx, gw, client.MergeFrom(gwOld)); err != nil { | ||
return errors.Wrapf(err, "failed to update gw status: %v", k8s.NamespacedName(gw)) | ||
} | ||
} | ||
|
||
// TODO: Listener status ListenerStatus | ||
// https://github.com/aws/aws-application-networking-k8s/blob/main/pkg/controllers/gateway_controller.go#L350 | ||
if requeueNeeded { | ||
return runtime.NewRequeueNeededAfter(requeueMessage, statusUpdateRequeueTime) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (r *gatewayReconciler) updateGatewayStatusFailure(ctx context.Context, gw *gwv1.Gateway, reason gwv1.GatewayConditionReason, err error) error { | ||
gwOld := gw.DeepCopy() | ||
|
||
needPatch := r.gatewayConditionUpdater(gw, string(gwv1.GatewayConditionAccepted), metav1.ConditionFalse, string(reason), err.Error()) | ||
|
||
if needPatch { | ||
if err := r.k8sClient.Status().Patch(ctx, gw, client.MergeFrom(gwOld)); err != nil { | ||
return errors.Wrapf(err, "failed to update gw status: %v", k8s.NamespacedName(gw)) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
@@ -475,3 +522,12 @@ func (r *gatewayReconciler) setupNLBGatewayControllerWatches(ctrl controller.Con | |
return nil | ||
|
||
} | ||
|
||
func isGatewayProgrammed(lbStatus elbv2model.LoadBalancerStatus) bool { | ||
if lbStatus.ProvisioningState == nil { | ||
return false | ||
} | ||
|
||
return lbStatus.ProvisioningState.Code == elbv2types.LoadBalancerStateEnumActive || lbStatus.ProvisioningState.Code == elbv2types.LoadBalancerStateEnumActiveImpaired | ||
|
||
} |
Uh oh!
There was an error while loading. Please reload this page.