Copilot commented on code in PR #745: URL: https://github.com/apache/dubbo-go-pixiu/pull/745#discussion_r2387062293
########## pixiu-ingress-controller/internal/controller/gatewayclass_controller.go: ########## @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package controller + +import ( + "context" + "fmt" + "time" +) + +import ( + "github.com/go-logr/logr" + + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + + "k8s.io/client-go/tools/record" + + "pixiu-ingress-controller/internal/controller/config" + "pixiu-ingress-controller/internal/controller/indexer" + "pixiu-ingress-controller/internal/controller/status" + + "pixiu-ingress-controller/internal/utils" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +const ( + FinalizerGatewayClassProtection = "pixiu.apache.org/gc-protection" +) + +// GatewayClassReconciler reconciles a GatewayClass object. +type GatewayClassReconciler struct { //nolint:revive + client.Client + Scheme *runtime.Scheme + record.EventRecorder + Log logr.Logger + Updater status.Updater +} + +// SetupWithManager sets up the controller with the Manager. +func (r *GatewayClassReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.EventRecorder = mgr.GetEventRecorderFor("gatewayclass-controller") + return ctrl.NewControllerManagedBy(mgr). + For(&gatewayv1.GatewayClass{}). + WithEventFilter(predicate.NewPredicateFuncs(r.GatewayClassFilter)). + WithEventFilter(predicate.GenerationChangedPredicate{}). + Complete(r) +} + +func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + gc := new(gatewayv1.GatewayClass) + if err := r.Get(ctx, req.NamespacedName, gc); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if gc.GetDeletionTimestamp().IsZero() { + if !controllerutil.ContainsFinalizer(gc, FinalizerGatewayClassProtection) { + controllerutil.AddFinalizer(gc, FinalizerGatewayClassProtection) + if err := r.Update(ctx, gc); err != nil { + return ctrl.Result{}, err + } + } + } else { + if controllerutil.ContainsFinalizer(gc, FinalizerGatewayClassProtection) { + var gatewayList gatewayv1.GatewayList + if err := r.List(ctx, &gatewayList, client.MatchingFields{indexer.GatewayClassIndexRef: gc.Name}); err != nil { + r.Log.Error(err, "failed to list gateways") + return ctrl.Result{}, err + } + if len(gatewayList.Items) > 0 { + var gateways []types.NamespacedName + for _, item := range gatewayList.Items { + gateways = append(gateways, types.NamespacedName{ + Namespace: item.GetNamespace(), + Name: item.GetName(), + }) + } + r.Eventf(gc, "Warning", "DeletionBlocked", "the GatewayClass is still used by Gateways: %v", gateways) + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + } else { + controllerutil.RemoveFinalizer(gc, FinalizerGatewayClassProtection) + if err := r.Update(ctx, gc); err != nil { + return ctrl.Result{}, err + } + } + } + + return ctrl.Result{}, nil + } + + condition := meta.Condition{ + Type: string(gatewayv1.GatewayClassConditionStatusAccepted), + Status: meta.ConditionTrue, + Reason: string(gatewayv1.GatewayClassReasonAccepted), + ObservedGeneration: gc.Generation, + Message: "the gatewayclass has been accepted by the apisix-ingress-controller", Review Comment: The error message references 'apisix-ingress-controller' but this is a Pixiu ingress controller. Should be 'pixiu-ingress-controller' to match the project name. ```suggestion Message: "the gatewayclass has been accepted by the pixiu-ingress-controller", ``` ########## pixiu-ingress-controller/Dockerfile: ########## @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build the manager binary +FROM golang:1.24 AS builder +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY cmd/main.go cmd/main.go Review Comment: The Dockerfile attempts to copy 'cmd/main.go' but the actual main file is located at 'cmd/pixiuctl/main.go'. This will cause the Docker build to fail. ########## pixiu-ingress-controller/internal/controller/utils.go: ########## @@ -0,0 +1,731 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package controller + +import ( + "context" + "errors" + "fmt" + "path" + "reflect" + "strings" +) + +import ( + "github.com/go-logr/logr" + + "github.com/samber/lo" + + corev1 "k8s.io/api/core/v1" + + networkingv1 "k8s.io/api/networking/v1" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + + "pixiu-ingress-controller/api/v1alpha1" + + "pixiu-ingress-controller/internal/controller/config" + "pixiu-ingress-controller/internal/controller/indexer" + + "pixiu-ingress-controller/internal/types" + + "pixiu-ingress-controller/internal/utils" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + KindGateway = "Gateway" + KindGatewayClass = "GatewayClass" + KindIngress = "Ingress" + KindIngressClass = "IngressClass" + KindGatewayProxy = "GatewayProxy" + KindSecret = "Secret" +) + +var ( + enableReferenceGrant bool +) + +func SetEnableReferenceGrant(enable bool) { + enableReferenceGrant = enable +} + +func GetEnableReferenceGrant() bool { + return enableReferenceGrant +} + +func TypePredicate[T client.Object]() func(obj client.Object) bool { + return func(obj client.Object) bool { + _, ok := obj.(T) + return ok + } +} + +func IsConditionPresentAndEqual(conditions []metav1.Condition, condition metav1.Condition) bool { + for _, cond := range conditions { + if cond.Type == condition.Type && + cond.Reason == condition.Reason && + cond.Status == condition.Status && + cond.ObservedGeneration == condition.ObservedGeneration { + return true + } + } + return false +} + +func ListRequests(ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, opts ...client.ListOption) []reconcile.Request { + return ListMatchingRequests(ctx, c, logger, listObj, func(obj client.Object) bool { return true }, opts...) +} + +func ListMatchingRequests(ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, matchFunc func(obj client.Object) bool, opts ...client.ListOption) []reconcile.Request { + if err := c.List(ctx, listObj, opts...); err != nil { + logger.Error(err, "failed to list resource") + return nil + } + + items, err := meta.ExtractList(listObj) + if err != nil { + logger.Error(err, "failed to extract list items") + return nil + } + + var requests []reconcile.Request + for _, item := range items { + obj, ok := item.(client.Object) + if !ok { + continue + } + + if matchFunc(obj) { + requests = append(requests, reconcile.Request{ + NamespacedName: utils.NamespacedName(obj), + }) + } + } + return requests +} + +func ProcessIngressClassParameters(c client.Client, log logr.Logger, object client.Object, ingressClass *networkingv1.IngressClass) error { + if ingressClass == nil || ingressClass.Spec.Parameters == nil { + return nil + } + + parameters := ingressClass.Spec.Parameters + // check if the parameters reference GatewayProxy + if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy { + ns := object.GetNamespace() + if parameters.Namespace != nil { + ns = *parameters.Namespace + } + + gatewayProxy := &v1alpha1.GatewayProxy{} + if err := c.Get(context.TODO(), client.ObjectKey{ + Namespace: ns, + Name: parameters.Name, + }, gatewayProxy); err != nil { + log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) + return err + } + } + + return nil +} + +func GetIngressClass(ctx context.Context, c client.Client, log logr.Logger, ingressClassName string) (*networkingv1.IngressClass, error) { + if ingressClassName == "" { + // Check for default ingress class + ingressClassList := &networkingv1.IngressClassList{} + if err := c.List(ctx, ingressClassList, client.MatchingFields{ + indexer.IngressClass: config.GetControllerName(), + }); err != nil { + log.Error(err, "failed to list ingress classes") + return nil, err + } + + // Find the ingress class that is marked as default + for _, ic := range ingressClassList.Items { + if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { + return &ic, nil + } + } + return nil, errors.New("no default ingress class found") + } + + // Check if the specified ingress class is controlled by us + var ingressClass networkingv1.IngressClass + if err := c.Get(ctx, client.ObjectKey{Name: ingressClassName}, &ingressClass); err != nil { + return nil, err + } + + if matchesController(ingressClass.Spec.Controller) { + return &ingressClass, nil + } + + return nil, errors.New("ingress class is not controlled by use") +} + +func GetGatewayProxyByIngressClass(ctx context.Context, r client.Client, ingressClass *networkingv1.IngressClass) (*v1alpha1.GatewayProxy, error) { + if ingressClass.Spec.Parameters == nil { + return nil, nil + } + + if ingressClass.Spec.Parameters.APIGroup == nil || + *ingressClass.Spec.Parameters.APIGroup != v1alpha1.GroupVersion.Group || + ingressClass.Spec.Parameters.Kind != KindGatewayProxy { + return nil, nil + } + + namespace := ingressClass.Namespace + if ingressClass.Spec.Parameters.Namespace != nil { + namespace = *ingressClass.Spec.Parameters.Namespace + } + + gatewayProxy := new(v1alpha1.GatewayProxy) + if err := r.Get(ctx, client.ObjectKey{ + Namespace: namespace, + Name: ingressClass.Spec.Parameters.Name, + }, gatewayProxy); err != nil { + return nil, fmt.Errorf("failed to get gateway proxy: %w", err) + } + return gatewayProxy, nil +} + +func GetGatewayProxyByGateway(ctx context.Context, r client.Client, gateway *gatewayv1.Gateway) (*v1alpha1.GatewayProxy, error) { + if gateway == nil { + return nil, nil + } + infra := gateway.Spec.Infrastructure + if infra == nil || infra.ParametersRef == nil { + return nil, nil + } + + ns := gateway.GetNamespace() + paramRef := infra.ParametersRef + if string(paramRef.Group) != v1alpha1.GroupVersion.Group || string(paramRef.Kind) != KindGatewayProxy { + return nil, nil + } + gatewayProxy := &v1alpha1.GatewayProxy{} + if err := r.Get(context.Background(), client.ObjectKey{ + Namespace: ns, + Name: paramRef.Name, + }, gatewayProxy); err != nil { + return nil, fmt.Errorf("failed to get GatewayProxy: %w", err) + } + return gatewayProxy, nil +} + +func getListenerStatus(ctx context.Context, mrgc client.Client, gateway *gatewayv1.Gateway) ([]gatewayv1.ListenerStatus, error) { + statuses := make(map[gatewayv1.SectionName]gatewayv1.ListenerStatus, len(gateway.Spec.Listeners)) + + for i, listener := range gateway.Spec.Listeners { + attachedRoutes, err := getAttachedRoutesForListener(ctx, mrgc, *gateway, listener) + if err != nil { + return nil, err + } + var ( + now = metav1.Now() + conditionProgrammed = metav1.Condition{ + Type: string(gatewayv1.ListenerConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.GetGeneration(), + LastTransitionTime: now, + Reason: string(gatewayv1.ListenerReasonProgrammed), + } + conditionAccepted = metav1.Condition{ + Type: string(gatewayv1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.GetGeneration(), + LastTransitionTime: now, + Reason: string(gatewayv1.ListenerReasonAccepted), + } + conditionConflicted = metav1.Condition{ + Type: string(gatewayv1.ListenerConditionConflicted), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.GetGeneration(), + LastTransitionTime: now, + Reason: string(gatewayv1.ListenerReasonNoConflicts), + } + conditionResolvedRefs = metav1.Condition{ + Type: string(gatewayv1.ListenerConditionResolvedRefs), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.GetGeneration(), + LastTransitionTime: now, + Reason: string(gatewayv1.ListenerReasonResolvedRefs), + } + + supportedKinds = []gatewayv1.RouteGroupKind{} + ) + + status := gatewayv1.ListenerStatus{ + Name: listener.Name, + Conditions: []metav1.Condition{ + conditionProgrammed, + conditionAccepted, + conditionConflicted, + conditionResolvedRefs, + }, + SupportedKinds: supportedKinds, + AttachedRoutes: attachedRoutes, + } + + changed := false + if len(gateway.Status.Listeners) > i { + if gateway.Status.Listeners[i].AttachedRoutes != attachedRoutes { + changed = true + } + for _, condition := range status.Conditions { + if !IsConditionPresentAndEqual(gateway.Status.Listeners[i].Conditions, condition) { + changed = true + break + } + } + } else { + changed = true + } + + if changed { + statuses[listener.Name] = status + } else { + statuses[listener.Name] = gateway.Status.Listeners[i] + } + } + + // check for conflicts + + statusArray := []gatewayv1.ListenerStatus{} + for _, status := range statuses { + statusArray = append(statusArray, status) + } + + return statusArray, nil +} + +func getAttachedRoutesForListener(ctx context.Context, mgrc client.Client, gateway gatewayv1.Gateway, listener gatewayv1.Listener) (int32, error) { + httpRouteList := gatewayv1.HTTPRouteList{} + if err := mgrc.List(ctx, &httpRouteList); err != nil { + return 0, err + } + var attachedRoutes int32 + for _, route := range httpRouteList.Items { + route := route + acceptedByGateway := lo.ContainsBy(route.Status.Parents, func(parentStatus gatewayv1.RouteParentStatus) bool { + parentRef := parentStatus.ParentRef + if parentRef.Group != nil && *parentRef.Group != gatewayv1.GroupName { + return false + } + if parentRef.Kind != nil && *parentRef.Kind != KindGateway { + return false + } + gatewayNamespace := route.Namespace + if parentRef.Namespace != nil { + gatewayNamespace = string(*parentRef.Namespace) + } + return gateway.Namespace == gatewayNamespace && gateway.Name == string(parentRef.Name) + }) + if !acceptedByGateway { + continue + } + + for _, parentRef := range route.Spec.ParentRefs { + ok, _, err := checkRouteAcceptedByListener( + ctx, + mgrc, + &route, + gateway, + listener, + parentRef, + ) + if err != nil { + return 0, err + } + if ok { + attachedRoutes++ + } + } + } + return attachedRoutes, nil +} + +func SetGatewayConditionAccepted(gw *gatewayv1.Gateway, status bool, message string) (ok bool) { + condition := metav1.Condition{ + Type: string(gatewayv1.GatewayConditionAccepted), + Status: ConditionStatus(status), + Reason: string(gatewayv1.GatewayReasonAccepted), + ObservedGeneration: gw.GetGeneration(), + Message: message, + LastTransitionTime: metav1.Now(), + } + + if !IsConditionPresentAndEqual(gw.Status.Conditions, condition) { + setGatewayCondition(gw, condition) + ok = true + } + return +} + +func ConditionStatus(status bool) metav1.ConditionStatus { + if status { + return metav1.ConditionTrue + } + return metav1.ConditionFalse +} + +func setGatewayCondition(gw *gatewayv1.Gateway, newCondition metav1.Condition) { + gw.Status.Conditions = MergeCondition(gw.Status.Conditions, newCondition) +} + +func acceptedMessage(kind string) string { + return fmt.Sprintf("the %s has been accepted by the apisix-ingress-controller", kind) Review Comment: The acceptance message references 'apisix-ingress-controller' but this is a Pixiu ingress controller. Should be 'pixiu-ingress-controller' to match the project name. ```suggestion return fmt.Sprintf("the %s has been accepted by the pixiu-ingress-controller", kind) ``` ########## pixiu-ingress-controller/Makefile: ########## @@ -0,0 +1,263 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +GATEAY_API_VERSION ?= v1.3.0 + +# Image URL to use all building/pushing image targets +IMG ?= controller:latest + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk command is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet setup-envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# CertManager is installed by default; skip with: +# - CERT_MANAGER_INSTALL_SKIP=true +KIND_CLUSTER ?= pixiu-ingress-controller-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify + +##@ Build + +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager /cmd/pixiuctl/main.go Review Comment: Invalid path in go build command. Should be './cmd/pixiuctl/main.go' instead of '/cmd/pixiuctl/main.go' (missing the leading dot and extra slash). ```suggestion go build -o bin/manager ./cmd/pixiuctl/main.go ``` ########## pixiu-ingress-controller/internal/controller/utils.go: ########## @@ -0,0 +1,731 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package controller + +import ( + "context" + "errors" + "fmt" + "path" + "reflect" + "strings" +) + +import ( + "github.com/go-logr/logr" + + "github.com/samber/lo" + + corev1 "k8s.io/api/core/v1" + + networkingv1 "k8s.io/api/networking/v1" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + + "pixiu-ingress-controller/api/v1alpha1" + + "pixiu-ingress-controller/internal/controller/config" + "pixiu-ingress-controller/internal/controller/indexer" + + "pixiu-ingress-controller/internal/types" + + "pixiu-ingress-controller/internal/utils" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + KindGateway = "Gateway" + KindGatewayClass = "GatewayClass" + KindIngress = "Ingress" + KindIngressClass = "IngressClass" + KindGatewayProxy = "GatewayProxy" + KindSecret = "Secret" +) + +var ( + enableReferenceGrant bool +) + +func SetEnableReferenceGrant(enable bool) { + enableReferenceGrant = enable +} + +func GetEnableReferenceGrant() bool { + return enableReferenceGrant +} + +func TypePredicate[T client.Object]() func(obj client.Object) bool { + return func(obj client.Object) bool { + _, ok := obj.(T) + return ok + } +} + +func IsConditionPresentAndEqual(conditions []metav1.Condition, condition metav1.Condition) bool { + for _, cond := range conditions { + if cond.Type == condition.Type && + cond.Reason == condition.Reason && + cond.Status == condition.Status && + cond.ObservedGeneration == condition.ObservedGeneration { + return true + } + } + return false +} + +func ListRequests(ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, opts ...client.ListOption) []reconcile.Request { + return ListMatchingRequests(ctx, c, logger, listObj, func(obj client.Object) bool { return true }, opts...) +} + +func ListMatchingRequests(ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, matchFunc func(obj client.Object) bool, opts ...client.ListOption) []reconcile.Request { + if err := c.List(ctx, listObj, opts...); err != nil { + logger.Error(err, "failed to list resource") + return nil + } + + items, err := meta.ExtractList(listObj) + if err != nil { + logger.Error(err, "failed to extract list items") + return nil + } + + var requests []reconcile.Request + for _, item := range items { + obj, ok := item.(client.Object) + if !ok { + continue + } + + if matchFunc(obj) { + requests = append(requests, reconcile.Request{ + NamespacedName: utils.NamespacedName(obj), + }) + } + } + return requests +} + +func ProcessIngressClassParameters(c client.Client, log logr.Logger, object client.Object, ingressClass *networkingv1.IngressClass) error { + if ingressClass == nil || ingressClass.Spec.Parameters == nil { + return nil + } + + parameters := ingressClass.Spec.Parameters + // check if the parameters reference GatewayProxy + if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy { + ns := object.GetNamespace() + if parameters.Namespace != nil { + ns = *parameters.Namespace + } + + gatewayProxy := &v1alpha1.GatewayProxy{} + if err := c.Get(context.TODO(), client.ObjectKey{ + Namespace: ns, + Name: parameters.Name, + }, gatewayProxy); err != nil { + log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) + return err + } + } + + return nil +} + +func GetIngressClass(ctx context.Context, c client.Client, log logr.Logger, ingressClassName string) (*networkingv1.IngressClass, error) { + if ingressClassName == "" { + // Check for default ingress class + ingressClassList := &networkingv1.IngressClassList{} + if err := c.List(ctx, ingressClassList, client.MatchingFields{ + indexer.IngressClass: config.GetControllerName(), + }); err != nil { + log.Error(err, "failed to list ingress classes") + return nil, err + } + + // Find the ingress class that is marked as default + for _, ic := range ingressClassList.Items { + if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { + return &ic, nil + } + } + return nil, errors.New("no default ingress class found") + } + + // Check if the specified ingress class is controlled by us + var ingressClass networkingv1.IngressClass + if err := c.Get(ctx, client.ObjectKey{Name: ingressClassName}, &ingressClass); err != nil { + return nil, err + } + + if matchesController(ingressClass.Spec.Controller) { + return &ingressClass, nil + } + + return nil, errors.New("ingress class is not controlled by use") Review Comment: Grammar error: 'controlled by use' should be 'controlled by us'. ```suggestion return nil, errors.New("ingress class is not controlled by us") ``` ########## pixiu-ingress-controller/Makefile: ########## @@ -0,0 +1,263 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +GATEAY_API_VERSION ?= v1.3.0 Review Comment: Variable name has a typo: 'GATEAY_API_VERSION' should be 'GATEWAY_API_VERSION'. ```suggestion GATEWAY_API_VERSION ?= v1.3.0 ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
