diff --git a/.github/workflows/commands.yaml b/.github/workflows/commands.yaml index a5ffffc..fbce5e6 100644 --- a/.github/workflows/commands.yaml +++ b/.github/workflows/commands.yaml @@ -12,11 +12,9 @@ on: types: [checks_requested] jobs: - generate-httproute: - name: Run kuadrantctl generate gatewayapi httproute + build-and-test: + name: Build and test kuadrantctl commands runs-on: ubuntu-latest - env: - KIND_CLUSTER_NAME: kuadrantctl-local steps: - name: Set up Go 1.23.x uses: actions/setup-go@v4 @@ -28,9 +26,12 @@ jobs: - name: build run: | make install - - name: run command + - name: run help command run: | - bin/kuadrantctl generate gatewayapi httproute --oas examples/oas3/gateway-api-petstore.yaml + bin/kuadrantctl --help + - name: run version command + run: | + bin/kuadrantctl version required-checks: name: Command Testing Required Checks @@ -38,7 +39,7 @@ jobs: # If a new check is added in this file, and it should be retested on entry to the merge queue, # it needs to be added to the list below aka needs: [ existing check 1, existing check 2, new check ]. needs: - - generate-httproute + - build-and-test if: always() runs-on: ubuntu-latest steps: diff --git a/cmd/diagnose.go b/cmd/diagnose.go new file mode 100644 index 0000000..6ed3d2b --- /dev/null +++ b/cmd/diagnose.go @@ -0,0 +1,632 @@ +package cmd + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + kuadrantv1 "github.com/kuadrant/kuadrant-operator/api/v1" + kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1" + kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" +) + +var ( + diagnoseNamespace string + diagnoseAllNamespaces bool + diagnoseOutputFormat string +) + +type DiagnosticReport struct { + GatewayIssues []ResourceIssue + HTTPRouteIssues []ResourceIssue + AuthPolicyIssues []ResourceIssue + RateLimitPolicyIssues []ResourceIssue + TokenRateLimitPolicyIssues []ResourceIssue + DNSPolicyIssues []ResourceIssue + TLSPolicyIssues []ResourceIssue + KuadrantIssues []ResourceIssue + Summary DiagnosticSummary +} + +type ResourceIssue struct { + ResourceType string + Namespace string + Name string + Issues []string + Status string +} + +type DiagnosticSummary struct { + TotalGateways int + UnprogrammedGateways int + TotalHTTPRoutes int + UnacceptedHTTPRoutes int + TotalAuthPolicies int + UnenforcedAuthPolicies int + TotalRateLimitPolicies int + UnenforcedRateLimitPolicies int + TotalTokenRateLimitPolicies int + UnenforcedTokenRateLimitPolicies int + TotalDNSPolicies int + UnenforcedDNSPolicies int + TotalTLSPolicies int + UnenforcedTLSPolicies int + TotalKuadrants int + UnreadyKuadrants int +} + +// checkPolicyConditions checks standard Accepted and Enforced conditions on a policy +// Returns a list of issues and whether the policy is enforced +func checkPolicyConditions(conditions []metav1.Condition) (issues []string, isEnforced bool) { + for _, condition := range conditions { + switch condition.Type { + case "Accepted": + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Not Accepted: %s - %s", condition.Reason, condition.Message)) + } + case "Enforced": + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Not Enforced: %s - %s", condition.Reason, condition.Message)) + } else { + isEnforced = true + } + } + } + return issues, isEnforced +} + +func diagnoseCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "diagnose", + Short: "Diagnose Kuadrant and Gateway API resources", + Long: "Analyze Kuadrant and Gateway API resources for issues, including unprogrammed gateways, unenforced policies, and configuration problems", + RunE: runDiagnose, + } + + cmd.Flags().StringVarP(&diagnoseNamespace, "namespace", "n", "", "Namespace to diagnose (default: all namespaces)") + cmd.Flags().BoolVarP(&diagnoseAllNamespaces, "all-namespaces", "A", false, "Diagnose resources from all namespaces") + cmd.Flags().StringVarP(&diagnoseOutputFormat, "output", "o", "text", "Output format: text, yaml, or json") + + return cmd +} + +func runDiagnose(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + logger := logf.FromContext(ctx) + + k8sClient, err := newK8sClient() + if err != nil { + return err + } + + // Determine namespace scope + var namespaceOption client.ListOption + if diagnoseAllNamespaces || diagnoseNamespace == "" { + namespaceOption = client.InNamespace("") + logger.Info("Diagnosing resources from all namespaces") + } else { + namespaceOption = client.InNamespace(diagnoseNamespace) + logger.Info("Diagnosing resources", "namespace", diagnoseNamespace) + } + + report := DiagnosticReport{} + + // Define all diagnose functions to run + diagnoseFuncs := []struct { + name string + fn func(context.Context, client.Client, *DiagnosticReport, client.ListOption) error + }{ + {"gateways", diagnoseGateways}, + {"HTTPRoutes", diagnoseHTTPRoutes}, + {"AuthPolicies", diagnoseAuthPolicies}, + {"RateLimitPolicies", diagnoseRateLimitPolicies}, + {"TokenRateLimitPolicies", diagnoseTokenRateLimitPolicies}, + {"DNSPolicies", diagnoseDNSPolicies}, + {"TLSPolicies", diagnoseTLSPolicies}, + {"Kuadrant CRs", diagnoseKuadrants}, + } + + // Run all diagnose functions + for _, df := range diagnoseFuncs { + if err := df.fn(ctx, k8sClient, &report, namespaceOption); err != nil { + logger.Error(err, "Failed to diagnose", "type", df.name) + // Continue with other resource types + } + } + + // Print report + printDiagnosticReport(report) + + return nil +} + +func diagnoseGateways(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + gatewayList := &gatewayapiv1.GatewayList{} + if err := k8sClient.List(ctx, gatewayList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalGateways = len(gatewayList.Items) + + for _, gateway := range gatewayList.Items { + issues := []string{} + isProgrammed := false + isAccepted := false + + // Check status conditions + for _, condition := range gateway.Status.Conditions { + switch condition.Type { + case string(gatewayapiv1.GatewayConditionProgrammed): + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Not Programmed: %s - %s", condition.Reason, condition.Message)) + } else { + isProgrammed = true + } + case string(gatewayapiv1.GatewayConditionAccepted): + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Not Accepted: %s - %s", condition.Reason, condition.Message)) + } else { + isAccepted = true + } + case string(gatewayapiv1.GatewayConditionReady): + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Not Ready: %s - %s", condition.Reason, condition.Message)) + } + } + } + + // Check listener statuses + // Only check positive conditions (conditions that should be True for healthy state) + for _, listener := range gateway.Status.Listeners { + for _, condition := range listener.Conditions { + // Only report issues for conditions that should be True + // Negative conditions like "Conflicted" or "Detached" being False is healthy + switch condition.Type { + case string(gatewayapiv1.ListenerConditionAccepted), + string(gatewayapiv1.ListenerConditionProgrammed), + string(gatewayapiv1.ListenerConditionResolvedRefs): + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Listener %s - %s: %s - %s", + listener.Name, condition.Type, condition.Reason, condition.Message)) + } + } + } + } + + // Count unprogrammed gateways once per gateway + if !isProgrammed { + report.Summary.UnprogrammedGateways++ + } + + if len(issues) > 0 { + var status string + if isProgrammed && isAccepted { + status = "Programmed+Accepted (with warnings)" + } else if !isProgrammed { + status = "Not Programmed" + } else if !isAccepted { + status = "Not Accepted" + } else { + status = "Unknown" + } + + report.GatewayIssues = append(report.GatewayIssues, ResourceIssue{ + ResourceType: "Gateway", + Namespace: gateway.Namespace, + Name: gateway.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseHTTPRoutes(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + routeList := &gatewayapiv1.HTTPRouteList{} + if err := k8sClient.List(ctx, routeList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalHTTPRoutes = len(routeList.Items) + + for _, route := range routeList.Items { + issues := []string{} + isAccepted := false + + // Check parent status + for _, parentStatus := range route.Status.Parents { + for _, condition := range parentStatus.Conditions { + if condition.Type == string(gatewayapiv1.RouteConditionAccepted) { + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Not Accepted by parent %s: %s - %s", + parentStatus.ParentRef.Name, condition.Reason, condition.Message)) + } else { + isAccepted = true + } + } else if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("Parent %s - %s: %s - %s", + parentStatus.ParentRef.Name, condition.Type, condition.Reason, condition.Message)) + } + } + } + + // Check if route has any parent refs + if len(route.Spec.ParentRefs) == 0 { + issues = append(issues, "No parent gateways configured") + } + + // Count unaccepted routes once per route + if !isAccepted && len(issues) > 0 { + report.Summary.UnacceptedHTTPRoutes++ + } + + if len(issues) > 0 { + var status string + if isAccepted { + status = "Accepted (with warnings)" + } else { + status = "Not Accepted" + } + + report.HTTPRouteIssues = append(report.HTTPRouteIssues, ResourceIssue{ + ResourceType: "HTTPRoute", + Namespace: route.Namespace, + Name: route.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseAuthPolicies(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + policyList := &kuadrantv1.AuthPolicyList{} + if err := k8sClient.List(ctx, policyList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalAuthPolicies = len(policyList.Items) + + for _, policy := range policyList.Items { + // Check status conditions + conditionIssues, isEnforced := checkPolicyConditions(policy.Status.Conditions) + issues := conditionIssues + + if !isEnforced { + report.Summary.UnenforcedAuthPolicies++ + } + + // Check if target ref is set + if policy.Spec.TargetRef.Name == "" { + issues = append(issues, "No target reference configured") + } + + if len(issues) > 0 { + var status string + if isEnforced { + status = "Enforced (with warnings)" + } else { + status = "Not Enforced" + } + + report.AuthPolicyIssues = append(report.AuthPolicyIssues, ResourceIssue{ + ResourceType: "AuthPolicy", + Namespace: policy.Namespace, + Name: policy.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseRateLimitPolicies(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + policyList := &kuadrantv1.RateLimitPolicyList{} + if err := k8sClient.List(ctx, policyList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalRateLimitPolicies = len(policyList.Items) + + for _, policy := range policyList.Items { + // Check status conditions + conditionIssues, isEnforced := checkPolicyConditions(policy.Status.Conditions) + issues := conditionIssues + + if !isEnforced { + report.Summary.UnenforcedRateLimitPolicies++ + } + + // Check if target ref is set + if policy.Spec.TargetRef.Name == "" { + issues = append(issues, "No target reference configured") + } + + if len(issues) > 0 { + var status string + if isEnforced { + status = "Enforced (with warnings)" + } else { + status = "Not Enforced" + } + + report.RateLimitPolicyIssues = append(report.RateLimitPolicyIssues, ResourceIssue{ + ResourceType: "RateLimitPolicy", + Namespace: policy.Namespace, + Name: policy.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseTokenRateLimitPolicies(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + policyList := &kuadrantv1alpha1.TokenRateLimitPolicyList{} + if err := k8sClient.List(ctx, policyList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalTokenRateLimitPolicies = len(policyList.Items) + + for _, policy := range policyList.Items { + // Check status conditions + conditionIssues, isEnforced := checkPolicyConditions(policy.Status.Conditions) + issues := conditionIssues + + if !isEnforced { + report.Summary.UnenforcedTokenRateLimitPolicies++ + } + + // Check if target ref is set + if policy.Spec.TargetRef.Name == "" { + issues = append(issues, "No target reference configured") + } + + if len(issues) > 0 { + var status string + if isEnforced { + status = "Enforced (with warnings)" + } else { + status = "Not Enforced" + } + + report.TokenRateLimitPolicyIssues = append(report.TokenRateLimitPolicyIssues, ResourceIssue{ + ResourceType: "TokenRateLimitPolicy", + Namespace: policy.Namespace, + Name: policy.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseDNSPolicies(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + policyList := &kuadrantv1.DNSPolicyList{} + if err := k8sClient.List(ctx, policyList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalDNSPolicies = len(policyList.Items) + + for _, policy := range policyList.Items { + // Check status conditions + conditionIssues, isEnforced := checkPolicyConditions(policy.Status.Conditions) + issues := conditionIssues + + if !isEnforced { + report.Summary.UnenforcedDNSPolicies++ + } + + // Check if target ref is set + if policy.Spec.TargetRef.Name == "" { + issues = append(issues, "No target reference configured") + } + + if len(issues) > 0 { + var status string + if isEnforced { + status = "Enforced (with warnings)" + } else { + status = "Not Enforced" + } + + report.DNSPolicyIssues = append(report.DNSPolicyIssues, ResourceIssue{ + ResourceType: "DNSPolicy", + Namespace: policy.Namespace, + Name: policy.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseTLSPolicies(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + policyList := &kuadrantv1.TLSPolicyList{} + if err := k8sClient.List(ctx, policyList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalTLSPolicies = len(policyList.Items) + + for _, policy := range policyList.Items { + // Check status conditions + conditionIssues, isEnforced := checkPolicyConditions(policy.Status.Conditions) + issues := conditionIssues + + if !isEnforced { + report.Summary.UnenforcedTLSPolicies++ + } + + // Check if target ref is set + if policy.Spec.TargetRef.Name == "" { + issues = append(issues, "No target reference configured") + } + + if len(issues) > 0 { + var status string + if isEnforced { + status = "Enforced (with warnings)" + } else { + status = "Not Enforced" + } + + report.TLSPolicyIssues = append(report.TLSPolicyIssues, ResourceIssue{ + ResourceType: "TLSPolicy", + Namespace: policy.Namespace, + Name: policy.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func diagnoseKuadrants(ctx context.Context, k8sClient client.Client, report *DiagnosticReport, namespaceOption client.ListOption) error { + kuadrantList := &kuadrantv1beta1.KuadrantList{} + if err := k8sClient.List(ctx, kuadrantList, namespaceOption); err != nil { + return err + } + + report.Summary.TotalKuadrants = len(kuadrantList.Items) + + for _, kuadrant := range kuadrantList.Items { + issues := []string{} + isReady := false + + // Check status conditions + for _, condition := range kuadrant.Status.Conditions { + if string(condition.Status) != string(metav1.ConditionTrue) { + issues = append(issues, fmt.Sprintf("%s: %s - %s", condition.Type, condition.Reason, condition.Message)) + if condition.Type == "Ready" { + report.Summary.UnreadyKuadrants++ + } + } else if condition.Type == "Ready" { + isReady = true + } + } + + if len(issues) > 0 { + var status string + if isReady { + status = "Ready (with warnings)" + } else { + status = "Not Ready" + } + + report.KuadrantIssues = append(report.KuadrantIssues, ResourceIssue{ + ResourceType: "Kuadrant", + Namespace: kuadrant.Namespace, + Name: kuadrant.Name, + Issues: issues, + Status: status, + }) + } + } + + return nil +} + +func printDiagnosticReport(report DiagnosticReport) { + fmt.Println("\n" + strings.Repeat("=", 80)) + fmt.Println("KUADRANT DIAGNOSTIC REPORT") + fmt.Println(strings.Repeat("=", 80)) + + // Print summary + fmt.Println("\nSUMMARY:") + fmt.Println(strings.Repeat("-", 80)) + + summaryItems := []struct { + label string + total int + unhealthy int + statusWord string + }{ + {"Gateways", report.Summary.TotalGateways, report.Summary.UnprogrammedGateways, "unprogrammed"}, + {"HTTPRoutes", report.Summary.TotalHTTPRoutes, report.Summary.UnacceptedHTTPRoutes, "unaccepted"}, + {"AuthPolicies", report.Summary.TotalAuthPolicies, report.Summary.UnenforcedAuthPolicies, "unenforced"}, + {"RateLimitPolicies", report.Summary.TotalRateLimitPolicies, report.Summary.UnenforcedRateLimitPolicies, "unenforced"}, + {"TokenRateLimitPolicies", report.Summary.TotalTokenRateLimitPolicies, report.Summary.UnenforcedTokenRateLimitPolicies, "unenforced"}, + {"DNSPolicies", report.Summary.TotalDNSPolicies, report.Summary.UnenforcedDNSPolicies, "unenforced"}, + {"TLSPolicies", report.Summary.TotalTLSPolicies, report.Summary.UnenforcedTLSPolicies, "unenforced"}, + {"Kuadrants", report.Summary.TotalKuadrants, report.Summary.UnreadyKuadrants, "unready"}, + } + + for _, item := range summaryItems { + fmt.Printf("%-27s %d total, %d %s\n", item.label+":", item.total, item.unhealthy, item.statusWord) + } + + // Count total issues + totalIssues := len(report.GatewayIssues) + len(report.HTTPRouteIssues) + + len(report.AuthPolicyIssues) + len(report.RateLimitPolicyIssues) + + len(report.TokenRateLimitPolicyIssues) + len(report.DNSPolicyIssues) + + len(report.TLSPolicyIssues) + len(report.KuadrantIssues) + + if totalIssues == 0 { + fmt.Println("\n" + strings.Repeat("=", 80)) + fmt.Println("✓ No issues found! All resources are healthy.") + fmt.Println(strings.Repeat("=", 80)) + return + } + + fmt.Printf("\nTotal issues found: %d\n", totalIssues) + + // Print all resource issues + issueGroups := []struct { + title string + issues []ResourceIssue + }{ + {"GATEWAY ISSUES:", report.GatewayIssues}, + {"HTTPROUTE ISSUES:", report.HTTPRouteIssues}, + {"AUTHPOLICY ISSUES:", report.AuthPolicyIssues}, + {"RATELIMITPOLICY ISSUES:", report.RateLimitPolicyIssues}, + {"TOKENRATELIMITPOLICY ISSUES:", report.TokenRateLimitPolicyIssues}, + {"DNSPOLICY ISSUES:", report.DNSPolicyIssues}, + {"TLSPOLICY ISSUES:", report.TLSPolicyIssues}, + {"KUADRANT ISSUES:", report.KuadrantIssues}, + } + + for _, group := range issueGroups { + if len(group.issues) > 0 { + fmt.Println("\n" + strings.Repeat("-", 80)) + fmt.Println(group.title) + fmt.Println(strings.Repeat("-", 80)) + for _, issue := range group.issues { + printResourceIssue(issue) + } + } + } + + fmt.Println("\n" + strings.Repeat("=", 80)) +} + +func printResourceIssue(issue ResourceIssue) { + fmt.Printf("\n Resource: %s/%s\n", issue.Namespace, issue.Name) + fmt.Printf(" Status: %s\n", issue.Status) + fmt.Println(" Issues:") + for _, iss := range issue.Issues { + fmt.Printf(" - %s\n", iss) + } +} diff --git a/cmd/dump.go b/cmd/dump.go new file mode 100644 index 0000000..444f08d --- /dev/null +++ b/cmd/dump.go @@ -0,0 +1,178 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "path/filepath" + "reflect" + "time" + + "github.com/spf13/cobra" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/yaml" + + kuadrantv1 "github.com/kuadrant/kuadrant-operator/api/v1" + kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1" + kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" +) + +var ( + dumpNamespace string + dumpAllNamespaces bool + dumpOutputDir string + dumpForce bool +) + +func dumpCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "dump", + Short: "Dump Kuadrant and Gateway API resources", + Long: "Dump all Kuadrant and Gateway API resources to files for investigation and debugging", + RunE: runDump, + } + + cmd.Flags().StringVarP(&dumpNamespace, "namespace", "n", "", "Namespace to dump resources from") + cmd.Flags().BoolVarP(&dumpAllNamespaces, "all-namespaces", "A", false, "Explicitly dump resources from all namespaces (default if no namespace specified)") + cmd.Flags().StringVarP(&dumpOutputDir, "output", "o", "", "Output directory (default: ./kuadrant-dump-)") + cmd.Flags().BoolVar(&dumpForce, "force", false, "Force overwrite if output directory exists and is not empty") + + return cmd +} + +func runDump(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + logger := logf.FromContext(ctx) + + // Validate flags + if dumpNamespace != "" && dumpAllNamespaces { + return fmt.Errorf("cannot specify both --namespace and --all-namespaces") + } + + if dumpOutputDir == "" { + timestamp := time.Now().Format("20060102-150405") + dumpOutputDir = fmt.Sprintf("kuadrant-dump-%s", timestamp) + } + + // Check if directory exists and is not empty (unless --force is used) + if !dumpForce { + if info, err := os.Stat(dumpOutputDir); err == nil && info.IsDir() { + entries, err := os.ReadDir(dumpOutputDir) + if err != nil { + return fmt.Errorf("failed to read output directory: %w", err) + } + if len(entries) > 0 { + return fmt.Errorf("output directory %s already exists and is not empty (use --force to overwrite)", dumpOutputDir) + } + } + } + + if err := os.MkdirAll(dumpOutputDir, 0755); err != nil { + return fmt.Errorf("failed to create output directory: %w", err) + } + + logger.Info("Dumping resources", "output", dumpOutputDir) + + k8sClient, err := newK8sClient() + if err != nil { + return err + } + + // Determine namespace scope + var namespaceOption client.ListOption + if dumpAllNamespaces || dumpNamespace == "" { + namespaceOption = client.InNamespace("") + logger.Info("Dumping resources from all namespaces") + } else { + namespaceOption = client.InNamespace(dumpNamespace) + logger.Info("Dumping resources", "namespace", dumpNamespace) + } + + // Dump resources + resourceTypes := []struct { + name string + listObj client.ObjectList + }{ + {"gateways", &gatewayapiv1.GatewayList{}}, + {"gatewayclasses", &gatewayapiv1.GatewayClassList{}}, + {"httproutes", &gatewayapiv1.HTTPRouteList{}}, + {"authpolicies", &kuadrantv1.AuthPolicyList{}}, + {"ratelimitpolicies", &kuadrantv1.RateLimitPolicyList{}}, + {"tokenratelimitpolicies", &kuadrantv1alpha1.TokenRateLimitPolicyList{}}, + {"dnspolicies", &kuadrantv1.DNSPolicyList{}}, + {"tlspolicies", &kuadrantv1.TLSPolicyList{}}, + {"kuadrants", &kuadrantv1beta1.KuadrantList{}}, + } + + for _, rt := range resourceTypes { + if err := dumpResourceTypeGeneric(ctx, k8sClient, rt.name, rt.listObj, namespaceOption); err != nil { + logger.Error(err, "Failed to dump resource type", "type", rt.name) + // Continue with other resource types + } + } + + logger.Info("Dump completed successfully", "output", dumpOutputDir) + fmt.Printf("\nResources dumped to: %s\n", dumpOutputDir) + + return nil +} + +func dumpResourceTypeGeneric( + ctx context.Context, + k8sClient client.Client, + resourceType string, + listObj client.ObjectList, + namespaceOption client.ListOption, +) error { + logger := logf.FromContext(ctx) + + if err := k8sClient.List(ctx, listObj, namespaceOption); err != nil { + return fmt.Errorf("failed to list %s: %w", resourceType, err) + } + + listValue := reflect.ValueOf(listObj).Elem() + itemsField := listValue.FieldByName("Items") + if !itemsField.IsValid() { + return fmt.Errorf("list type for %s does not have Items field", resourceType) + } + + itemsLen := itemsField.Len() + if itemsLen == 0 { + logger.V(1).Info("No resources found", "type", resourceType) + return nil + } + + logger.Info("Dumping resources", "type", resourceType, "count", itemsLen) + + for i := 0; i < itemsLen; i++ { + item := itemsField.Index(i).Addr().Interface() + obj := item.(client.Object) + + filename := fmt.Sprintf("%s-%s.yaml", obj.GetNamespace(), obj.GetName()) + if obj.GetNamespace() == "" { + filename = fmt.Sprintf("%s.yaml", obj.GetName()) + } + + filePath := filepath.Join(dumpOutputDir, resourceType, filename) + + data, err := yaml.Marshal(item) + if err != nil { + logger.Error(err, "Failed to marshal resource", "name", obj.GetName(), "namespace", obj.GetNamespace()) + continue + } + + if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil { + logger.Error(err, "Failed to create directory", "path", filepath.Dir(filePath)) + continue + } + + if err := os.WriteFile(filePath, data, 0644); err != nil { + logger.Error(err, "Failed to write file", "file", filePath) + continue + } + } + + return nil +} diff --git a/cmd/generate.go b/cmd/generate.go deleted file mode 100644 index 0bd67b9..0000000 --- a/cmd/generate.go +++ /dev/null @@ -1,18 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func generateCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "generate", - Short: "Commands related to kubernetes object generation", - Long: "Commands related to kubernetes object generation", - } - - cmd.AddCommand(generateKuadrantCommand()) - cmd.AddCommand(generateGatewayAPICommand()) - - return cmd -} diff --git a/cmd/generate_gatewayapi.go b/cmd/generate_gatewayapi.go deleted file mode 100644 index 8c6e583..0000000 --- a/cmd/generate_gatewayapi.go +++ /dev/null @@ -1,17 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func generateGatewayAPICommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "gatewayapi", - Short: "Generate Gataway API resources", - Long: "Generate Gataway API resources", - } - - cmd.AddCommand(generateGatewayApiHttpRouteCommand()) - - return cmd -} diff --git a/cmd/generate_gatewayapi_httproute.go b/cmd/generate_gatewayapi_httproute.go deleted file mode 100644 index dd30417..0000000 --- a/cmd/generate_gatewayapi_httproute.go +++ /dev/null @@ -1,94 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - - "github.com/getkin/kin-openapi/openapi3" - "github.com/ghodss/yaml" - "github.com/kuadrant/kuadrantctl/pkg/gatewayapi" - "github.com/kuadrant/kuadrantctl/pkg/utils" - "github.com/spf13/cobra" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" -) - -var ( - generateGatewayAPIHTTPRouteOAS string - generateGatewayAPIHTTPRouteFormat string -) - -//kuadrantctl generate gatewayapi httproute --oas [OAS_FILE_PATH | OAS_URL | @] - -func generateGatewayApiHttpRouteCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "httproute", - Short: "Generate Gateway API HTTPRoute from OpenAPI 3.0.X", - Long: "Generate Gateway API HTTPRoute from OpenAPI 3.0.X", - RunE: runGenerateGatewayApiHttpRoute, - } - - // OpenAPI ref - cmd.Flags().StringVar(&generateGatewayAPIHTTPRouteOAS, "oas", "", "Path to OpenAPI spec file (in JSON or YAML format), URL, or '-' to read from standard input (required)") - cmd.Flags().StringVarP(&generateGatewayAPIHTTPRouteFormat, "output-format", "o", "yaml", "Output format: 'yaml' or 'json'.") - err := cmd.MarkFlagRequired("oas") - if err != nil { - panic(err) - } - - return cmd -} - -func runGenerateGatewayApiHttpRoute(cmd *cobra.Command, args []string) error { - oasDataRaw, err := utils.ReadExternalResource(generateGatewayAPIHTTPRouteOAS) - if err != nil { - return err - } - - openapiLoader := openapi3.NewLoader() - doc, err := openapiLoader.LoadFromData(oasDataRaw) - if err != nil { - return err - } - - err = doc.Validate(openapiLoader.Context) - if err != nil { - return fmt.Errorf("OpenAPI validation error: %w", err) - } - - httpRoute := buildHTTPRoute(doc) - jsonBytes, err := json.Marshal(httpRoute) - if err != nil { - return err - } - - var outputBytes []byte - if generateGatewayAPIHTTPRouteFormat == "json" { - outputBytes = jsonBytes - } else { - outputBytes, err = yaml.JSONToYAML(jsonBytes) // use `omitempty`'s from the json Marshal - if err != nil { - return err - } - } - - fmt.Fprintln(cmd.OutOrStdout(), string(outputBytes)) - return nil -} - -func buildHTTPRoute(doc *openapi3.T) *gatewayapiv1.HTTPRoute { - return &gatewayapiv1.HTTPRoute{ - TypeMeta: v1.TypeMeta{ - APIVersion: gatewayapiv1.GroupVersion.String(), - Kind: "HTTPRoute", - }, - ObjectMeta: gatewayapi.HTTPRouteObjectMetaFromOAS(doc), - Spec: gatewayapiv1.HTTPRouteSpec{ - CommonRouteSpec: gatewayapiv1.CommonRouteSpec{ - ParentRefs: gatewayapi.HTTPRouteGatewayParentRefsFromOAS(doc), - }, - Hostnames: gatewayapi.HTTPRouteHostnamesFromOAS(doc), - Rules: gatewayapi.HTTPRouteRulesFromOAS(doc), - }, - } -} diff --git a/cmd/generate_gatewayapi_httproute_test.go b/cmd/generate_gatewayapi_httproute_test.go deleted file mode 100644 index 49666de..0000000 --- a/cmd/generate_gatewayapi_httproute_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package cmd - -import ( - "bytes" - "io" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - "sigs.k8s.io/yaml" -) - -var _ = Describe("Generate HTTPRoute", func() { - var ( - cmd *cobra.Command - cmdStdoutBuffer *bytes.Buffer - cmdStderrBuffer *bytes.Buffer - ) - - BeforeEach(func() { - cmd = generateGatewayApiHttpRouteCommand() - cmdStdoutBuffer = bytes.NewBufferString("") - cmdStderrBuffer = bytes.NewBufferString("") - cmd.SetOut(cmdStdoutBuffer) - cmd.SetErr(cmdStderrBuffer) - }) - - Context("with invalid OAS", func() { - It("happy path", func() { - cmd.SetArgs([]string{"--oas", "testdata/invalid_oas.yaml"}) - Expect(cmd.Execute()).Should(MatchError(ContainSubstring("OpenAPI validation error"))) - - }) - }) - - Context("with root level kuadrant extensions", func() { - It("HTTPRoute is generated", func() { - cmd.SetArgs([]string{"--oas", "testdata/petstore_openapi.yaml"}) - Expect(cmd.Execute()).ShouldNot(HaveOccurred()) - out, err := io.ReadAll(cmdStdoutBuffer) - Expect(err).ShouldNot(HaveOccurred()) - - var httpRoute gatewayapiv1.HTTPRoute - Expect(yaml.Unmarshal(out, &httpRoute)).ShouldNot(HaveOccurred()) - Expect(httpRoute.TypeMeta).To(Equal(metav1.TypeMeta{ - APIVersion: gatewayapiv1.GroupVersion.String(), - Kind: "HTTPRoute", - })) - Expect(httpRoute.ObjectMeta).To(Equal(metav1.ObjectMeta{ - Name: "petstore", - Namespace: "petstore-ns", - })) - Expect(httpRoute.Spec.CommonRouteSpec).To(Equal(gatewayapiv1.CommonRouteSpec{ - ParentRefs: []gatewayapiv1.ParentReference{ - { - Name: "gw", Namespace: ptr.To(gatewayapiv1.Namespace("gw-ns")), - }, - }, - })) - Expect(httpRoute.Spec.Hostnames).To(Equal([]gatewayapiv1.Hostname{ - gatewayapiv1.Hostname("example.com"), - })) - Expect(httpRoute.Spec.Rules).To(HaveLen(3)) - Expect(httpRoute.Spec.Rules).To(ContainElement( - gatewayapiv1.HTTPRouteRule{ - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/cat"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodGet), - }, - }, - BackendRefs: []gatewayapiv1.HTTPBackendRef{ - { - BackendRef: gatewayapiv1.BackendRef{ - BackendObjectReference: gatewayapiv1.BackendObjectReference{ - Name: "petstore", - Namespace: ptr.To(gatewayapiv1.Namespace("petstore")), - Port: ptr.To(gatewayapiv1.PortNumber(80)), - }, - }, - }, - }, - }, - )) - Expect(httpRoute.Spec.Rules).To(ContainElement( - gatewayapiv1.HTTPRouteRule{ - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/dog"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodGet), - }, - }, - BackendRefs: []gatewayapiv1.HTTPBackendRef{ - { - BackendRef: gatewayapiv1.BackendRef{ - BackendObjectReference: gatewayapiv1.BackendObjectReference{ - Name: "petstore", - Namespace: ptr.To(gatewayapiv1.Namespace("petstore")), - Port: ptr.To(gatewayapiv1.PortNumber(80)), - }, - }, - }, - }, - }, - )) - Expect(httpRoute.Spec.Rules).To(ContainElement( - gatewayapiv1.HTTPRouteRule{ - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/dog"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodPost), - }, - }, - BackendRefs: []gatewayapiv1.HTTPBackendRef{ - { - BackendRef: gatewayapiv1.BackendRef{ - BackendObjectReference: gatewayapiv1.BackendObjectReference{ - Name: "petstore", - Namespace: ptr.To(gatewayapiv1.Namespace("petstore")), - Port: ptr.To(gatewayapiv1.PortNumber(80)), - }, - }, - }, - }, - }, - )) - }) - }) -}) diff --git a/cmd/generate_kuadrant.go b/cmd/generate_kuadrant.go deleted file mode 100644 index 3e575d6..0000000 --- a/cmd/generate_kuadrant.go +++ /dev/null @@ -1,18 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func generateKuadrantCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "kuadrant", - Short: "Generate Kuadrant resources", - Long: "Generate Kuadrant resources", - } - - cmd.AddCommand(generateKuadrantRateLimitPolicyCommand()) - cmd.AddCommand(generateKuadrantAuthPolicyCommand()) - - return cmd -} diff --git a/cmd/generate_kuadrant_authpolicy.go b/cmd/generate_kuadrant_authpolicy.go deleted file mode 100644 index 9f849b7..0000000 --- a/cmd/generate_kuadrant_authpolicy.go +++ /dev/null @@ -1,115 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - - "github.com/getkin/kin-openapi/openapi3" - "github.com/ghodss/yaml" - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" - "github.com/spf13/cobra" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - - "github.com/kuadrant/kuadrantctl/pkg/gatewayapi" - "github.com/kuadrant/kuadrantctl/pkg/kuadrantapi" - "github.com/kuadrant/kuadrantctl/pkg/utils" -) - -var ( - generateAuthPolicyOAS string - generateAuthPolicyFormat string -) - -//kuadrantctl generate kuadrant authpolicy --oas [OAS_FILE_PATH | OAS_URL | @] - -func generateKuadrantAuthPolicyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "authpolicy", - Short: "Generate Kuadrant AuthPolicy from OpenAPI 3.0.X", - Long: "Generate Kuadrant AuthPolicy from OpenAPI 3.0.X", - RunE: runGenerateKuadrantAuthPolicy, - } - - // OpenAPI ref - cmd.Flags().StringVar(&generateAuthPolicyOAS, "oas", "", "Path to OpenAPI spec file (in JSON or YAML format), URL, or '-' to read from standard input (required)") - cmd.Flags().StringVarP(&generateAuthPolicyFormat, "output-format", "o", "yaml", "Output format: 'yaml' or 'json'.") - err := cmd.MarkFlagRequired("oas") - if err != nil { - panic(err) - } - - return cmd -} - -func runGenerateKuadrantAuthPolicy(cmd *cobra.Command, args []string) error { - oasDataRaw, err := utils.ReadExternalResource(generateAuthPolicyOAS) - if err != nil { - return err - } - - openapiLoader := openapi3.NewLoader() - doc, err := openapiLoader.LoadFromData(oasDataRaw) - if err != nil { - return err - } - - err = doc.Validate(openapiLoader.Context) - if err != nil { - return fmt.Errorf("OpenAPI validation error: %w", err) - } - - ap := buildAuthPolicy(doc) - jsonBytes, err := json.Marshal(ap) - if err != nil { - return err - } - - var outputBytes []byte - if generateAuthPolicyFormat == "json" { - outputBytes = jsonBytes - } else { - outputBytes, err = yaml.JSONToYAML(jsonBytes) // use `omitempty`'s from the json Marshal - if err != nil { - return err - } - } - - fmt.Fprintln(cmd.OutOrStdout(), string(outputBytes)) - return nil -} - -func buildAuthPolicy(doc *openapi3.T) *kuadrantapiv1beta2.AuthPolicy { - routeMeta := gatewayapi.HTTPRouteObjectMetaFromOAS(doc) - - ap := &kuadrantapiv1beta2.AuthPolicy{ - TypeMeta: v1.TypeMeta{ - APIVersion: "kuadrant.io/v1beta2", - Kind: "AuthPolicy", - }, - ObjectMeta: kuadrantapi.AuthPolicyObjectMetaFromOAS(doc), - Spec: kuadrantapiv1beta2.AuthPolicySpec{ - TargetRef: gatewayapiv1alpha2.PolicyTargetReference{ - Group: gatewayapiv1.GroupName, - Kind: gatewayapiv1.Kind("HTTPRoute"), - Name: gatewayapiv1.ObjectName(routeMeta.Name), - }, - // Currently only authentication rules enforced - AuthPolicyCommonSpec: kuadrantapiv1beta2.AuthPolicyCommonSpec{ - AuthScheme: &kuadrantapiv1beta2.AuthSchemeSpec{ - Authentication: kuadrantapi.AuthPolicyAuthenticationSchemeFromOAS(doc), - }, - RouteSelectors: kuadrantapi.AuthPolicyTopRouteSelectorsFromOAS(doc), - }, - }, - } - - if routeMeta.Namespace != "" { - ap.Spec.TargetRef.Namespace = &[]gatewayapiv1.Namespace{ - gatewayapiv1.Namespace(routeMeta.Namespace), - }[0] - } - - return ap -} diff --git a/cmd/generate_kuadrant_authpolicy_test.go b/cmd/generate_kuadrant_authpolicy_test.go deleted file mode 100644 index 8bde5de..0000000 --- a/cmd/generate_kuadrant_authpolicy_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package cmd - -import ( - "bytes" - "io" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/yaml" - - authorinoapi "github.com/kuadrant/authorino/api/v1beta2" - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" -) - -var _ = Describe("Generate AuthPolicy", func() { - var ( - cmd *cobra.Command - cmdStdoutBuffer *bytes.Buffer - cmdStderrBuffer *bytes.Buffer - ) - - BeforeEach(func() { - cmd = generateKuadrantAuthPolicyCommand() - cmdStdoutBuffer = bytes.NewBufferString("") - cmdStderrBuffer = bytes.NewBufferString("") - cmd.SetOut(cmdStdoutBuffer) - cmd.SetErr(cmdStderrBuffer) - }) - - Context("with invalid OAS", func() { - It("happy path", func() { - cmd.SetArgs([]string{"--oas", "testdata/invalid_oas.yaml"}) - Expect(cmd.Execute()).Should(MatchError(ContainSubstring("OpenAPI validation error"))) - - }) - }) - - Context("with operation including security", func() { - It("authorization policy generated", func() { - cmd.SetArgs([]string{"--oas", "testdata/petstore_openapi.yaml"}) - Expect(cmd.Execute()).ShouldNot(HaveOccurred()) - out, err := io.ReadAll(cmdStdoutBuffer) - Expect(err).ShouldNot(HaveOccurred()) - - var kap kuadrantapiv1beta2.AuthPolicy - Expect(yaml.Unmarshal(out, &kap)).ShouldNot(HaveOccurred()) - Expect(kap.TypeMeta).To(Equal(metav1.TypeMeta{ - APIVersion: kuadrantapiv1beta2.GroupVersion.String(), Kind: "AuthPolicy", - })) - Expect(kap.ObjectMeta).To(Equal(metav1.ObjectMeta{ - Name: "petstore", - Namespace: "petstore-ns", - })) - Expect(kap.Spec.TargetRef).To(Equal(gatewayapiv1alpha2.PolicyTargetReference{ - Group: gatewayapiv1.GroupName, - Kind: gatewayapiv1.Kind("HTTPRoute"), - Name: gatewayapiv1.ObjectName("petstore"), - Namespace: ptr.To(gatewayapiv1.Namespace("petstore-ns")), - })) - Expect(kap.Spec.AuthPolicyCommonSpec.RouteSelectors).To(HaveExactElements( - kuadrantapiv1beta2.RouteSelector{ - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/dog"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodPost), - }, - }, - }, - )) - Expect(kap.Spec.AuthPolicyCommonSpec.AuthScheme).To(Equal( - &kuadrantapiv1beta2.AuthSchemeSpec{ - Authentication: map[string]kuadrantapiv1beta2.AuthenticationSpec{ - "postDog_securedDog": kuadrantapiv1beta2.AuthenticationSpec{ - AuthenticationSpec: authorinoapi.AuthenticationSpec{ - Credentials: authorinoapi.Credentials{}, - AuthenticationMethodSpec: authorinoapi.AuthenticationMethodSpec{ - Jwt: &authorinoapi.JwtAuthenticationSpec{ - IssuerUrl: "https://example.com/.well-known/openid-configuration", - }, - }, - }, - CommonAuthRuleSpec: kuadrantapiv1beta2.CommonAuthRuleSpec{ - RouteSelectors: []kuadrantapiv1beta2.RouteSelector{ - { - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/dog"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodPost), - }, - }, - }, - }, - }, - }, - }, - }, - )) - }) - }) -}) diff --git a/cmd/generate_kuadrant_ratelimitpolicy.go b/cmd/generate_kuadrant_ratelimitpolicy.go deleted file mode 100644 index 458871d..0000000 --- a/cmd/generate_kuadrant_ratelimitpolicy.go +++ /dev/null @@ -1,114 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/getkin/kin-openapi/openapi3" - "github.com/ghodss/yaml" - - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" - "github.com/spf13/cobra" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - - "github.com/kuadrant/kuadrantctl/pkg/gatewayapi" - "github.com/kuadrant/kuadrantctl/pkg/kuadrantapi" - "github.com/kuadrant/kuadrantctl/pkg/utils" -) - -//kuadrantctl generate kuadrant ratelimitpolicy --oas [OAS_FILE_PATH | OAS_URL | @] - -var ( - generateRateLimitPolicyOAS string - generateRateLimitPolicyFormat string -) - -func generateKuadrantRateLimitPolicyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "ratelimitpolicy", - Short: "Generate Kuadrant Rate Limit Policy from OpenAPI 3.0.X", - Long: "Generate Kuadrant Rate Limit Policy from OpenAPI 3.0.X", - RunE: runGenerateKuadrantRateLimitPolicy, - } - - cmd.Flags().StringVar(&generateRateLimitPolicyOAS, "oas", "", "Path to OpenAPI spec file (in JSON or YAML format), URL, or '-' to read from standard input (required)") - cmd.Flags().StringVarP(&generateRateLimitPolicyFormat, "output-format", "o", "yaml", "Output format: 'yaml' or 'json'.") - - if err := cmd.MarkFlagRequired("oas"); err != nil { - fmt.Println("Error setting 'oas' flag as required:", err) - os.Exit(1) - } - - return cmd -} - -func runGenerateKuadrantRateLimitPolicy(cmd *cobra.Command, args []string) error { - oasDataRaw, err := utils.ReadExternalResource(generateRateLimitPolicyOAS) - if err != nil { - return err - } - - openapiLoader := openapi3.NewLoader() - doc, err := openapiLoader.LoadFromData(oasDataRaw) - if err != nil { - return err - } - - err = doc.Validate(openapiLoader.Context) - if err != nil { - return fmt.Errorf("OpenAPI validation error: %w", err) - } - - rlp := buildRateLimitPolicy(doc) - - jsonBytes, err := json.Marshal(rlp) - if err != nil { - return err - } - - var outputBytes []byte - if generateRateLimitPolicyFormat == "json" { - outputBytes = jsonBytes - } else { - outputBytes, err = yaml.JSONToYAML(jsonBytes) // use `omitempty`'s from the json Marshal - if err != nil { - return err - } - } - - fmt.Fprintln(cmd.OutOrStdout(), string(outputBytes)) - return nil -} - -func buildRateLimitPolicy(doc *openapi3.T) *kuadrantapiv1beta2.RateLimitPolicy { - routeMeta := gatewayapi.HTTPRouteObjectMetaFromOAS(doc) - - rlp := &kuadrantapiv1beta2.RateLimitPolicy{ - TypeMeta: v1.TypeMeta{ - APIVersion: "kuadrant.io/v1beta2", - Kind: "RateLimitPolicy", - }, - ObjectMeta: kuadrantapi.RateLimitPolicyObjectMetaFromOAS(doc), - Spec: kuadrantapiv1beta2.RateLimitPolicySpec{ - TargetRef: gatewayapiv1alpha2.PolicyTargetReference{ - Group: gatewayapiv1.GroupName, - Kind: gatewayapiv1.Kind("HTTPRoute"), - Name: gatewayapiv1.ObjectName(routeMeta.Name), - }, - RateLimitPolicyCommonSpec: kuadrantapiv1beta2.RateLimitPolicyCommonSpec{ - Limits: kuadrantapi.RateLimitPolicyLimitsFromOAS(doc), - }, - }, - } - - if routeMeta.Namespace != "" { - rlp.Spec.TargetRef.Namespace = &[]gatewayapiv1.Namespace{ - gatewayapiv1.Namespace(routeMeta.Namespace), - }[0] - } - - return rlp -} diff --git a/cmd/generate_kuadrant_ratelimitpolicy_test.go b/cmd/generate_kuadrant_ratelimitpolicy_test.go deleted file mode 100644 index ae71f55..0000000 --- a/cmd/generate_kuadrant_ratelimitpolicy_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package cmd - -import ( - "bytes" - "io" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/yaml" - - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" -) - -var _ = Describe("Generate Ratelimitpolicy", func() { - var ( - cmd *cobra.Command - cmdStdoutBuffer *bytes.Buffer - cmdStderrBuffer *bytes.Buffer - ) - - BeforeEach(func() { - cmd = generateKuadrantRateLimitPolicyCommand() - cmdStdoutBuffer = bytes.NewBufferString("") - cmdStderrBuffer = bytes.NewBufferString("") - cmd.SetOut(cmdStdoutBuffer) - cmd.SetErr(cmdStderrBuffer) - }) - - Context("with invalid OAS", func() { - It("happy path", func() { - cmd.SetArgs([]string{"--oas", "testdata/invalid_oas.yaml"}) - Expect(cmd.Execute()).Should(MatchError(ContainSubstring("OpenAPI validation error"))) - - }) - }) - - Context("with rate limiting kuadrant extensions", func() { - It("rate limit policy generated", func() { - cmd.SetArgs([]string{"--oas", "testdata/petstore_openapi.yaml"}) - Expect(cmd.Execute()).ShouldNot(HaveOccurred()) - out, err := io.ReadAll(cmdStdoutBuffer) - Expect(err).ShouldNot(HaveOccurred()) - - var rlp kuadrantapiv1beta2.RateLimitPolicy - Expect(yaml.Unmarshal(out, &rlp)).ShouldNot(HaveOccurred()) - Expect(rlp.TypeMeta).To(Equal(metav1.TypeMeta{ - APIVersion: kuadrantapiv1beta2.GroupVersion.String(), Kind: "RateLimitPolicy", - })) - Expect(rlp.ObjectMeta).To(Equal(metav1.ObjectMeta{ - Name: "petstore", - Namespace: "petstore-ns", - })) - Expect(rlp.Spec.TargetRef).To(Equal(gatewayapiv1alpha2.PolicyTargetReference{ - Group: gatewayapiv1.GroupName, - Kind: gatewayapiv1.Kind("HTTPRoute"), - Name: gatewayapiv1.ObjectName("petstore"), - Namespace: ptr.To(gatewayapiv1.Namespace("petstore-ns")), - })) - Expect(rlp.Spec.RateLimitPolicyCommonSpec.Limits).To(HaveLen(2)) - Expect(rlp.Spec.RateLimitPolicyCommonSpec.Limits).To(HaveKeyWithValue("getCat", kuadrantapiv1beta2.Limit{ - Counters: []kuadrantapiv1beta2.ContextSelector{ - "request.headers.x-forwarded-for", - }, - RouteSelectors: []kuadrantapiv1beta2.RouteSelector{ - { - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/cat"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodGet), - }, - }, - }, - }, - Rates: []kuadrantapiv1beta2.Rate{ - { - Limit: 1, - Duration: 10, - Unit: kuadrantapiv1beta2.TimeUnit("second"), - }, - }, - })) - Expect(rlp.Spec.RateLimitPolicyCommonSpec.Limits).To(HaveKeyWithValue("getDog", kuadrantapiv1beta2.Limit{ - Counters: []kuadrantapiv1beta2.ContextSelector{ - "request.headers.x-forwarded-for", - }, - RouteSelectors: []kuadrantapiv1beta2.RouteSelector{ - { - Matches: []gatewayapiv1.HTTPRouteMatch{ - { - Path: &gatewayapiv1.HTTPPathMatch{ - Type: ptr.To(gatewayapiv1.PathMatchExact), - Value: ptr.To("/v1/dog"), - }, - Method: ptr.To(gatewayapiv1.HTTPMethodGet), - }, - }, - }, - }, - Rates: []kuadrantapiv1beta2.Rate{ - { - Limit: 3, - Duration: 10, - Unit: kuadrantapiv1beta2.TimeUnit("second"), - }, - }, - })) - }) - }) -}) diff --git a/cmd/root.go b/cmd/root.go index a66c6f4..4d1ad5b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,18 +17,60 @@ package cmd import ( "context" + "fmt" "os" "os/exec" "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + kuadrantv1 "github.com/kuadrant/kuadrant-operator/api/v1" + kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1" + kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" ) var ( verbose bool ) +// newK8sClient creates a new Kubernetes client with Gateway API and Kuadrant schemes registered +func newK8sClient() (client.Client, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, fmt.Errorf("failed to get kubeconfig: %w", err) + } + + // Register Gateway API and Kuadrant schemes + schemesToRegister := []struct { + addFunc func(*runtime.Scheme) error + name string + }{ + {gatewayapiv1.Install, "Gateway API"}, + {kuadrantv1.AddToScheme, "Kuadrant v1 API"}, + {kuadrantv1alpha1.AddToScheme, "Kuadrant v1alpha1 API"}, + {kuadrantv1beta1.AddToScheme, "Kuadrant v1beta1 API"}, + } + + for _, s := range schemesToRegister { + if err := s.addFunc(scheme.Scheme); err != nil { + return nil, fmt.Errorf("failed to add %s to scheme: %w", s.name, err) + } + } + + k8sClient, err := client.New(cfg, client.Options{Scheme: scheme.Scheme}) + if err != nil { + return nil, fmt.Errorf("failed to create Kubernetes client: %w", err) + } + + return k8sClient, nil +} + // GetRootCmd returns the root of the cobra command-tree. func GetRootCmd(args []string) *cobra.Command { // rootCmd represents the base command when called without any subcommands @@ -49,8 +91,9 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output") rootCmd.AddCommand(versionCommand()) - rootCmd.AddCommand(generateCommand()) rootCmd.AddCommand(topologyCommand()) + rootCmd.AddCommand(dumpCommand()) + rootCmd.AddCommand(diagnoseCommand()) if isBinaryAvailable("kubectl-kuadrant_dns") { rootCmd.AddCommand(dnsCommand()) diff --git a/go.mod b/go.mod index 8723a18..5d452b3 100644 --- a/go.mod +++ b/go.mod @@ -1,98 +1,108 @@ module github.com/kuadrant/kuadrantctl -go 1.23.6 +go 1.24.6 + +toolchain go1.24.11 require ( - github.com/getkin/kin-openapi v0.120.0 - github.com/ghodss/yaml v1.0.0 github.com/goccy/go-graphviz v0.2.9 - github.com/kuadrant/authorino v0.15.0 - github.com/kuadrant/kuadrant-operator v0.7.1 - github.com/onsi/ginkgo/v2 v2.13.2 - github.com/onsi/gomega v1.30.0 - github.com/spf13/cobra v1.8.0 - k8s.io/api v0.28.4 - k8s.io/apimachinery v0.28.4 - k8s.io/client-go v0.28.4 - k8s.io/utils v0.0.0-20231127182322-b307cd553661 - sigs.k8s.io/controller-runtime v0.16.3 - sigs.k8s.io/gateway-api v1.0.1-0.20231204134048-c7da42e6eafc + github.com/kuadrant/kuadrant-operator v1.3.1 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 + github.com/spf13/cobra v1.9.1 + k8s.io/api v0.33.3 + k8s.io/apimachinery v0.33.3 + k8s.io/client-go v0.33.3 + sigs.k8s.io/controller-runtime v0.21.0 + sigs.k8s.io/gateway-api v1.2.1 sigs.k8s.io/yaml v1.4.0 ) require ( + cel.dev/expr v0.24.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cert-manager/cert-manager v1.16.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/disintegration/imaging v1.6.2 // indirect - github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch/v5 v5.7.0 // indirect + github.com/emicklei/dot v1.8.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/flopp/go-findfont v0.1.0 // indirect github.com/fogleman/gg v1.3.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect - github.com/go-logr/zapr v1.2.4 // indirect - github.com/go-openapi/jsonpointer v0.20.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 // indirect - github.com/google/uuid v1.4.0 // indirect - github.com/imdario/mergo v1.0.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.23.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kuadrant/authorino v0.22.0 // indirect + github.com/kuadrant/authorino-operator v0.21.0 // indirect + github.com/kuadrant/dns-operator v0.0.0-20250826105007-7a0e6d88f7bb // indirect + github.com/kuadrant/limitador-operator v0.15.0 // indirect + github.com/kuadrant/policy-machinery v0.7.1-0.20251119154946-1ca17a075dd2 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/martinlindhe/base36 v1.1.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.76.2 // indirect + github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect + github.com/samber/lo v1.47.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/pflag v1.0.7 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 // indirect github.com/tetratelabs/wazero v1.8.1 // indirect - github.com/tidwall/gjson v1.14.0 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect golang.org/x/image v0.21.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.19.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.11.0 // indirect + golang.org/x/tools v0.35.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.28.4 // indirect - k8s.io/component-base v0.28.4 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/apiextensions-apiserver v0.33.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect + sigs.k8s.io/external-dns v0.14.0 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect ) replace github.com/imdario/mergo => dario.cat/mergo v0.3.5 diff --git a/go.sum b/go.sum index d1530ee..50ae690 100644 --- a/go.sum +++ b/go.sum @@ -1,294 +1,277 @@ -dario.cat/mergo v0.3.5 h1:rybKppoxBoyv1JiXjzlqE4gdrhB0Xk/us0OW7yDEAl0= -dario.cat/mergo v0.3.5/go.mod h1:fvkCdyGtdx6UQvuEimZ9mB2dzc2AymrLoRgHC4lz6ec= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cert-manager/cert-manager v1.16.2 h1:c9UU2E+8XWGruyvC/mdpc1wuLddtgmNr8foKdP7a8Jg= +github.com/cert-manager/cert-manager v1.16.2/go.mod h1:MfLVTL45hFZsqmaT1O0+b2ugaNNQQZttSFV9hASHUb0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI= github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/datawire/dlib v1.3.0 h1:KkmyXU1kwm3oPBk1ypR70YbcOlEXWzEbx5RE0iRXTGk= +github.com/datawire/dlib v1.3.0/go.mod h1:NiGDmetmbkBvtznpWSx6C0vA0s0LK9aHna3LJDqjruk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/elliotchance/orderedmap/v2 v2.2.0 h1:7/2iwO98kYT4XkOjA9mBEIwvi4KpGB4cyHeOFOnj4Vk= -github.com/elliotchance/orderedmap/v2 v2.2.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= -github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= -github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/emicklei/dot v1.8.0 h1:HnD60yAKFAevNeT+TPYr9pb8VB9bqdeSo0nzwIW6IOI= +github.com/emicklei/dot v1.8.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/flopp/go-findfont v0.1.0 h1:lPn0BymDUtJo+ZkV01VS3661HL6F4qFlkhcJN55u6mU= github.com/flopp/go-findfont v0.1.0/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= -github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= -github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-graphviz v0.2.9 h1:4yD2MIMpxNt+sOEARDh5jTE2S/jeAKi92w72B83mWGg= github.com/goccy/go-graphviz v0.2.9/go.mod h1:hssjl/qbvUXGmloY81BwXt2nqoApKo7DFgDj5dLJGb8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 h1:PxlBVtIFHR/mtWk2i0gTEdCz+jBnqiuHNSki0epDbVs= -github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kuadrant/authorino v0.15.0 h1:Xw/buh/wTINdL+IpLSxhlpet4hpleMxZzfx39c4VQng= -github.com/kuadrant/authorino v0.15.0/go.mod h1:vXkHKrntn8DR7kt8a8Ohxq+2lgAD0jWivThoP+7ASew= -github.com/kuadrant/kuadrant-operator v0.7.1 h1:sd3EnpeOjuc+mxLtzuMIlopuzYxaWm9bVcz/ZaF5Z8s= -github.com/kuadrant/kuadrant-operator v0.7.1/go.mod h1:yAhEoowC9DE0ribSjDJHiMHPH8VoBpOMYU3q5x06N3k= -github.com/kuadrant/limitador-operator v0.7.0 h1:pLIpM6vUxAY/Jn6ny61IGpqS7Oti786duBzJ67DJOuA= -github.com/kuadrant/limitador-operator v0.7.0/go.mod h1:tg+G+3eTzUUfvUmdbiqH3FnScEPSWZ3DmorD1ZAx1bo= +github.com/kuadrant/authorino v0.22.0 h1:jJVVs7S4uRM/ZkidBaThyYvMl2u5ExZzruo9OJ1tIu8= +github.com/kuadrant/authorino v0.22.0/go.mod h1:ABfDIfRebQ1cuL2qW+lrwwjN9t5aO1sjAdyd8lR4mKk= +github.com/kuadrant/authorino-operator v0.21.0 h1:wjb0lAF7ZzP8Rgc7rbJkGf+1PL2StSVDVdZ+/LjD3D8= +github.com/kuadrant/authorino-operator v0.21.0/go.mod h1:elsjAwIFdLDDu9ljq5S28LJIekhtVqeCA7AlaqPTL3U= +github.com/kuadrant/dns-operator v0.0.0-20250826105007-7a0e6d88f7bb h1:YTWt8bn7xi5T7XJ9+MlrMDnWk5d8AVMVWVePvHkvR9k= +github.com/kuadrant/dns-operator v0.0.0-20250826105007-7a0e6d88f7bb/go.mod h1:EF37SlMqbarJieimWDPRv/N5BACbdzLa9nCBJcoeFvs= +github.com/kuadrant/kuadrant-operator v1.3.1 h1:/dRHPf9JSIrvlQ+J7NdyKuryUAH/5zr5BijGyOmFLh0= +github.com/kuadrant/kuadrant-operator v1.3.1/go.mod h1:HvCXp3YLdbuPCxJQwmEUixAA4haWjaZHUGt1ZTtLlQM= +github.com/kuadrant/limitador-operator v0.15.0 h1:BWgYKV0iasFY3+zKQhLpTdfIlI2pD4MuGr8Hc30ypfg= +github.com/kuadrant/limitador-operator v0.15.0/go.mod h1:58b5gdSemjXUijd2TBPKBwaQskU7rynHGHD7rTqk2OE= +github.com/kuadrant/policy-machinery v0.7.1-0.20251119154946-1ca17a075dd2 h1:82Qf4klsgQ/176P8PoDbno8Jlik/WPg4j2l3pp1nKG8= +github.com/kuadrant/policy-machinery v0.7.1-0.20251119154946-1ca17a075dd2/go.mod h1:8JF2bof6DJsEVSQOxpi8Kuj/zEps+BezpRZpF3XRAa4= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg= +github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= -github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= -github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.76.2 h1:BpGDC87A2SaxbKgONsFLEX3kRcRJee2aLQbjXsuz0hA= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.76.2/go.mod h1:Rd8YnCqz+2FYsiGmE2DMlaLjQRB4v2jFNnzCt9YY4IM= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/telepresenceio/telepresence/rpc/v2 v2.6.8 h1:q5V85LBT9bA/c4YPa/kMvJGyKZDgBPJTftlAMqJx7j4= +github.com/telepresenceio/telepresence/rpc/v2 v2.6.8/go.mod h1:VlgfRoXaW6Tl8IZbHmMWhITne8HY09/wOFtABHGj3ic= +github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 h1:GMw3nEaOVyi+tNiGko5kAeRtoiEIpXNHmISyZ7fpw14= +github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7/go.mod h1:ihJ97e2gsd8GuzFF/I3B1qcik3XZLpXjumQifXi8Slg= github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550= github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= -github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= -github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= -golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= -k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= -k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU= -k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM= -k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= -k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= -k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= -k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= -k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo= -k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a h1:ZeIPbyHHqahGIbeyLJJjAUhnxCKqXaDY+n89Ms8szyA= -k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= -k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= -sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= -sigs.k8s.io/gateway-api v1.0.1-0.20231204134048-c7da42e6eafc h1:Ls/BrmdKJVBi4LVYhK4a4xA+5TO0mt66f6UpgTHk2Lc= -sigs.k8s.io/gateway-api v1.0.1-0.20231204134048-c7da42e6eafc/go.mod h1:i4fiyKUGk0zC7PIaoykdwjfOePLpLIGGX9iab7uhl0o= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= +k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro= +k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/external-dns v0.14.0 h1:pgY3DdyoBei+ej1nyZUzRt9ECm9RRwb9s6/CPWe51tc= +sigs.k8s.io/external-dns v0.14.0/go.mod h1:d4Knr/BFz8U1Lc6yLhCzTRP6nJOz6fqR/MnqqJPcIlU= +sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM= +sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/pkg/gatewayapi/http_route.go b/pkg/gatewayapi/http_route.go deleted file mode 100644 index 2868e0a..0000000 --- a/pkg/gatewayapi/http_route.go +++ /dev/null @@ -1,135 +0,0 @@ -package gatewayapi - -import ( - "github.com/getkin/kin-openapi/openapi3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/kuadrant/kuadrantctl/pkg/utils" -) - -func HTTPRouteObjectMetaFromOAS(doc *openapi3.T) metav1.ObjectMeta { - kuadrantRootExtension, err := utils.NewKuadrantOASRootExtension(doc) - if err != nil { - panic(err) - } - - if kuadrantRootExtension == nil { - return metav1.ObjectMeta{} - } - - if kuadrantRootExtension.Route == nil { - panic("openapi root kuadrant extension route not found") - } - - if kuadrantRootExtension.Route.Name == nil { - panic("openapi root kuadrant extension route name not found") - } - - om := metav1.ObjectMeta{ - Name: *kuadrantRootExtension.Route.Name, - Labels: kuadrantRootExtension.Route.Labels, - } - - if kuadrantRootExtension.Route.Namespace != nil { - om.Namespace = *kuadrantRootExtension.Route.Namespace - } - - return om -} - -func HTTPRouteGatewayParentRefsFromOAS(doc *openapi3.T) []gatewayapiv1.ParentReference { - kuadrantRootExtension, err := utils.NewKuadrantOASRootExtension(doc) - if err != nil { - panic(err) - } - - if kuadrantRootExtension == nil { - return nil - } - - if kuadrantRootExtension.Route == nil { - panic("openapi root kuadrant extension route not found") - } - - return kuadrantRootExtension.Route.ParentRefs -} - -func HTTPRouteHostnamesFromOAS(doc *openapi3.T) []gatewayapiv1.Hostname { - kuadrantRootExtension, err := utils.NewKuadrantOASRootExtension(doc) - if err != nil { - panic(err) - } - - if kuadrantRootExtension == nil { - return nil - } - - if kuadrantRootExtension.Route == nil { - panic("openapi root kuadrant extension route not found") - } - - return kuadrantRootExtension.Route.Hostnames -} - -func HTTPRouteRulesFromOAS(doc *openapi3.T) []gatewayapiv1.HTTPRouteRule { - // Current implementation, one rule per operation - // TODO(eguzki): consider about grouping operations as HTTPRouteMatch objects in fewer HTTPRouteRule objects - rules := make([]gatewayapiv1.HTTPRouteRule, 0) - - basePath, err := utils.BasePathFromOpenAPI(doc) - if err != nil { - panic(err) - } - - // Paths - for path, pathItem := range doc.Paths { - kuadrantPathExtension, err := utils.NewKuadrantOASPathExtension(pathItem) - if err != nil { - panic(err) - } - - // Operations - for verb, operation := range pathItem.Operations() { - kuadrantOperationExtension, err := utils.NewKuadrantOASOperationExtension(operation) - if err != nil { - panic(err) - } - - if ptr.Deref(kuadrantOperationExtension.Disable, kuadrantPathExtension.IsDisabled()) { - // not enabled for the operation - continue - } - - // default backendrefs at the path level - backendRefs := kuadrantPathExtension.BackendRefs - if len(kuadrantOperationExtension.BackendRefs) > 0 { - backendRefs = kuadrantOperationExtension.BackendRefs - } - - // default pathMatchType at the path level - pathMatchType := ptr.Deref( - kuadrantOperationExtension.PathMatchType, - kuadrantPathExtension.GetPathMatchType(), - ) - - rules = append(rules, buildHTTPRouteRule(basePath, path, pathItem, verb, operation, backendRefs, pathMatchType)) - } - } - - if len(rules) == 0 { - return nil - } - - return rules -} - -func buildHTTPRouteRule(basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, backendRefs []gatewayapiv1.HTTPBackendRef, pathMatchType gatewayapiv1.PathMatchType) gatewayapiv1.HTTPRouteRule { - match := utils.OpenAPIMatcherFromOASOperations(basePath, path, pathItem, verb, op, pathMatchType) - - return gatewayapiv1.HTTPRouteRule{ - BackendRefs: backendRefs, - Matches: []gatewayapiv1.HTTPRouteMatch{match}, - } -} diff --git a/pkg/kuadrantapi/authpolicy.go b/pkg/kuadrantapi/authpolicy.go deleted file mode 100644 index 2c14316..0000000 --- a/pkg/kuadrantapi/authpolicy.go +++ /dev/null @@ -1,254 +0,0 @@ -package kuadrantapi - -import ( - "errors" - "fmt" - - "github.com/getkin/kin-openapi/openapi3" - authorinoapi "github.com/kuadrant/authorino/api/v1beta2" - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/kuadrant/kuadrantctl/pkg/gatewayapi" - "github.com/kuadrant/kuadrantctl/pkg/utils" -) - -const ( - APIKeySecretLabel = "kuadrant.io/apikeys-by" -) - -func AuthPolicyObjectMetaFromOAS(doc *openapi3.T) metav1.ObjectMeta { - return gatewayapi.HTTPRouteObjectMetaFromOAS(doc) -} - -func buildAuthPolicyRouteSelectors(basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, pathMatchType gatewayapiv1.PathMatchType) []kuadrantapiv1beta2.RouteSelector { - match := utils.OpenAPIMatcherFromOASOperations(basePath, path, pathItem, verb, op, pathMatchType) - - return []kuadrantapiv1beta2.RouteSelector{ - { - Matches: []gatewayapiv1.HTTPRouteMatch{match}, - }, - } -} - -func AuthPolicyTopRouteSelectorsFromOAS(doc *openapi3.T) []kuadrantapiv1beta2.RouteSelector { - routeSelectors := make([]kuadrantapiv1beta2.RouteSelector, 0) - - basePath, err := utils.BasePathFromOpenAPI(doc) - if err != nil { - panic(err) - } - - for path, pathItem := range doc.Paths { - kuadrantPathExtension, err := utils.NewKuadrantOASPathExtension(pathItem) - if err != nil { - panic(err) - } - - // Operations - for verb, operation := range pathItem.Operations() { - kuadrantOperationExtension, err := utils.NewKuadrantOASOperationExtension(operation) - if err != nil { - panic(err) - } - - if ptr.Deref(kuadrantOperationExtension.Disable, kuadrantPathExtension.IsDisabled()) { - // not enabled for the operation - //fmt.Printf("OUT not enabled: path: %s, method: %s\n", path, verb) - continue - } - - // Get operation level security requirements or fallback to global security requirements - secRequirements := ptr.Deref(operation.Security, doc.Security) - - // Top RouteSelectors define the matching rules to call external auth service - // group together any routes that has at least one security requirement - if len(secRequirements) == 0 { - // no security - continue - } - - // default pathMatchType at the path level - pathMatchType := ptr.Deref( - kuadrantOperationExtension.PathMatchType, - kuadrantPathExtension.GetPathMatchType(), - ) - - routeSelectors = append(routeSelectors, buildAuthPolicyRouteSelectors(basePath, path, pathItem, verb, operation, pathMatchType)...) - } - } - - if len(routeSelectors) == 0 { - return nil - } - - return routeSelectors -} - -func AuthPolicyAuthenticationSchemeFromOAS(doc *openapi3.T) map[string]kuadrantapiv1beta2.AuthenticationSpec { - authentication := make(map[string]kuadrantapiv1beta2.AuthenticationSpec) - - basePath, err := utils.BasePathFromOpenAPI(doc) - if err != nil { - panic(err) - } - - // Paths - for path, pathItem := range doc.Paths { - kuadrantPathExtension, err := utils.NewKuadrantOASPathExtension(pathItem) - if err != nil { - panic(err) - } - - // Operations - for verb, operation := range pathItem.Operations() { - kuadrantOperationExtension, err := utils.NewKuadrantOASOperationExtension(operation) - if err != nil { - panic(err) - } - - if ptr.Deref(kuadrantOperationExtension.Disable, kuadrantPathExtension.IsDisabled()) { - // not enabled for the operation - //fmt.Printf("OUT not enabled: path: %s, method: %s\n", path, verb) - continue - } - - // Get operation level security requirements or fallback to global security requirements - secRequirements := ptr.Deref(operation.Security, doc.Security) - - if len(secRequirements) == 0 { - // no security - continue - } - - // default pathMatchType at the path level - pathMatchType := ptr.Deref( - kuadrantOperationExtension.PathMatchType, - kuadrantPathExtension.GetPathMatchType(), - ) - - operationAuthentication := buildOperationAuthentication(doc, basePath, path, pathItem, verb, operation, pathMatchType, secRequirements) - - // Aggregate auth methods per operation - authentication = utils.MergeMaps(authentication, operationAuthentication) - } - } - - if len(authentication) == 0 { - return nil - } - - return authentication -} - -func buildOperationAuthentication(doc *openapi3.T, basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, pathMatchType gatewayapiv1.PathMatchType, secRequirements openapi3.SecurityRequirements) map[string]kuadrantapiv1beta2.AuthenticationSpec { - // OpenAPI supports as security requirement to have multiple security schemes and ALL - // of the must be satisfied. - // From https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-requirement-object - // Kuadrant does not support it yet: https://github.com/Kuadrant/authorino/issues/112 - // not supported (AND'ed) - // security: - // - petstore_api_key: [] - // petstore_oidc: [] - // supported (OR'ed) - // security: - // - petstore_api_key: [] - // - petstore_oidc: [] - - opAuth := make(map[string]kuadrantapiv1beta2.AuthenticationSpec, 0) - for _, secReq := range secRequirements { - if len(secReq) > 1 { - panic(errors.New("multiple schemes that require ALL must be satisfied, currently not supported")) - } - - extractSecReqItemName := func(sr openapi3.SecurityRequirement) string { - for secReqItemName := range sr { - return secReqItemName - } - - return "" - } - - secReqItemName := extractSecReqItemName(secReq) - - secScheme, ok := doc.Components.SecuritySchemes[secReqItemName] - if !ok { - // should never happen. OpenAPI validation should detect this issue - continue - } - - if secScheme == nil || secScheme.Value == nil { - continue - } - - authName := fmt.Sprintf("%s_%s", utils.OpenAPIOperationName(path, verb, op), secReqItemName) - - // Ref https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#fixed-fields-23 - switch secScheme.Value.Type { - case "openIdConnect": - opAuth[authName] = openIDAuthenticationSpec(basePath, path, pathItem, verb, op, pathMatchType, *secScheme.Value) - case "apiKey": - opAuth[authName] = apiKeyAuthenticationSpec(basePath, path, pathItem, verb, op, pathMatchType, secReqItemName, *secScheme.Value) - } - } - - if len(opAuth) == 0 { - return nil - } - - return opAuth -} - -func apiKeyAuthenticationSpec(basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, pathMatchType gatewayapiv1.PathMatchType, secSchemeName string, secScheme openapi3.SecurityScheme) kuadrantapiv1beta2.AuthenticationSpec { - // From https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#fixed-fields-23 - // secScheme.In is required - // secScheme.Name is required - credentials := authorinoapi.Credentials{} - switch secScheme.In { - case "query": - credentials.QueryString = &authorinoapi.Named{Name: secScheme.Name} - case "header": - credentials.CustomHeader = &authorinoapi.CustomHeader{ - Named: authorinoapi.Named{Name: secScheme.Name}, - } - case "cookie": - credentials.Cookie = &authorinoapi.Named{Name: secScheme.Name} - } - - return kuadrantapiv1beta2.AuthenticationSpec{ - CommonAuthRuleSpec: kuadrantapiv1beta2.CommonAuthRuleSpec{ - RouteSelectors: buildAuthPolicyRouteSelectors(basePath, path, pathItem, verb, op, pathMatchType), - }, - AuthenticationSpec: authorinoapi.AuthenticationSpec{ - Credentials: credentials, - AuthenticationMethodSpec: authorinoapi.AuthenticationMethodSpec{ - ApiKey: &authorinoapi.ApiKeyAuthenticationSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - // label selector be like - // kuadrant.io/apikeys-by: ${SecuritySchemeName} - APIKeySecretLabel: secSchemeName, - }, - }, - }, - }, - }, - } -} - -func openIDAuthenticationSpec(basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, pathMatchType gatewayapiv1.PathMatchType, secScheme openapi3.SecurityScheme) kuadrantapiv1beta2.AuthenticationSpec { - return kuadrantapiv1beta2.AuthenticationSpec{ - CommonAuthRuleSpec: kuadrantapiv1beta2.CommonAuthRuleSpec{ - RouteSelectors: buildAuthPolicyRouteSelectors(basePath, path, pathItem, verb, op, pathMatchType), - }, - AuthenticationSpec: authorinoapi.AuthenticationSpec{ - AuthenticationMethodSpec: authorinoapi.AuthenticationMethodSpec{ - Jwt: &authorinoapi.JwtAuthenticationSpec{ - IssuerUrl: secScheme.OpenIdConnectUrl, - }, - }, - }, - } -} diff --git a/pkg/kuadrantapi/rate_limit_policy.go b/pkg/kuadrantapi/rate_limit_policy.go deleted file mode 100644 index 97cb2cd..0000000 --- a/pkg/kuadrantapi/rate_limit_policy.go +++ /dev/null @@ -1,93 +0,0 @@ -package kuadrantapi - -import ( - "github.com/getkin/kin-openapi/openapi3" - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/kuadrant/kuadrantctl/pkg/gatewayapi" - "github.com/kuadrant/kuadrantctl/pkg/utils" -) - -func RateLimitPolicyObjectMetaFromOAS(doc *openapi3.T) metav1.ObjectMeta { - return gatewayapi.HTTPRouteObjectMetaFromOAS(doc) -} - -func RateLimitPolicyLimitsFromOAS(doc *openapi3.T) map[string]kuadrantapiv1beta2.Limit { - // Current implementation, one limit per operation - // TODO(eguzki): consider about grouping operations in fewer RLP limits - - limits := make(map[string]kuadrantapiv1beta2.Limit) - - basePath, err := utils.BasePathFromOpenAPI(doc) - if err != nil { - panic(err) - } - - // Paths - for path, pathItem := range doc.Paths { - kuadrantPathExtension, err := utils.NewKuadrantOASPathExtension(pathItem) - if err != nil { - panic(err) - } - - // Operations - for verb, operation := range pathItem.Operations() { - kuadrantOperationExtension, err := utils.NewKuadrantOASOperationExtension(operation) - if err != nil { - panic(err) - } - - if ptr.Deref(kuadrantOperationExtension.Disable, kuadrantPathExtension.IsDisabled()) { - // not enabled for the operation - //fmt.Printf("OUT not enabled: path: %s, method: %s\n", path, verb) - continue - } - - // default backendrefs at the path level - rateLimit := kuadrantPathExtension.RateLimit - if kuadrantOperationExtension.RateLimit != nil { - rateLimit = kuadrantOperationExtension.RateLimit - } - - if rateLimit == nil { - // no rate limit defined for this operation - //fmt.Printf("OUT no rate limit defined: path: %s, method: %s\n", path, verb) - continue - } - - // default pathMatchType at the path level - pathMatchType := ptr.Deref( - kuadrantOperationExtension.PathMatchType, - kuadrantPathExtension.GetPathMatchType(), - ) - - limitName := utils.OpenAPIOperationName(path, verb, operation) - - limits[limitName] = kuadrantapiv1beta2.Limit{ - RouteSelectors: buildLimitRouteSelectors(basePath, path, pathItem, verb, operation, pathMatchType), - When: rateLimit.When, - Counters: rateLimit.Counters, - Rates: rateLimit.Rates, - } - } - } - - if len(limits) == 0 { - return nil - } - - return limits -} - -func buildLimitRouteSelectors(basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, pathMatchType gatewayapiv1.PathMatchType) []kuadrantapiv1beta2.RouteSelector { - match := utils.OpenAPIMatcherFromOASOperations(basePath, path, pathItem, verb, op, pathMatchType) - - return []kuadrantapiv1beta2.RouteSelector{ - { - Matches: []gatewayapiv1.HTTPRouteMatch{match}, - }, - } -} diff --git a/pkg/utils/external_resource_reader.go b/pkg/utils/external_resource_reader.go deleted file mode 100644 index db37ac1..0000000 --- a/pkg/utils/external_resource_reader.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed 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 utils - -import ( - "io" - "os" -) - -// ReadExternalResource reads data streams from external resources. Currently implemented: -// - '-' or '@' for STDIN -// - URLs (HTTP[S]) -// - Files -func ReadExternalResource(resource string) ([]byte, error) { - if resource == "-" || resource == "@" { - return io.ReadAll(os.Stdin) - } - - if url, isURL := ParseURL(resource); isURL { - return ReadURL(url) - } - - // Defaulting to filepath - return os.ReadFile(resource) -} diff --git a/pkg/utils/http_utils.go b/pkg/utils/http_utils.go deleted file mode 100644 index 9d2195c..0000000 --- a/pkg/utils/http_utils.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed 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 utils - -import ( - "io" - "net/http" - "net/url" -) - -// ParseURL returns true when valid HTTP[S] url is found -func ParseURL(str string) (*url.URL, bool) { - u, err := url.Parse(str) - return u, err == nil && u.Scheme != "" && u.Host != "" -} - -func ReadURL(location *url.URL) ([]byte, error) { - resp, err := http.Get(location.String()) - if err != nil { - return nil, err - } - data, err := io.ReadAll(resp.Body) - defer resp.Body.Close() - if err != nil { - return nil, err - } - return data, nil -} diff --git a/pkg/utils/http_utils_test.go b/pkg/utils/http_utils_test.go deleted file mode 100644 index d881786..0000000 --- a/pkg/utils/http_utils_test.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed 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 utils - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = DescribeTable("IsURL", - func(url string, expected bool) { - _, res := ParseURL(url) - Expect(res).To(Equal(expected)) - }, - Entry("Empty URL", "", false), - Entry("only schema", "https", false), - Entry("only schema", "https://", false), - Entry("only schema", "http://www", true), - Entry("only schema", "http://www.example.com/resources/a.yaml", true), - Entry("only schema", "https://www.example.com:443/resources/a.yaml", true), - Entry("only schema", "/home/testing-path.yaml", false), - Entry("only schema", "testing-path.yaml", false), - Entry("only schema", "alskjff#?asf//dfas", false), -) diff --git a/pkg/utils/kuadrant_oas_extension_types.go b/pkg/utils/kuadrant_oas_extension_types.go deleted file mode 100644 index 8d99028..0000000 --- a/pkg/utils/kuadrant_oas_extension_types.go +++ /dev/null @@ -1,111 +0,0 @@ -package utils - -import ( - "encoding/json" - - "github.com/getkin/kin-openapi/openapi3" - "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - kuadrantapiv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" -) - -type RouteObject struct { - Name *string `json:"name,omitempty"` - Namespace *string `json:"namespace,omitempty"` - Hostnames []gatewayapiv1.Hostname `json:"hostnames,omitempty"` - ParentRefs []gatewayapiv1.ParentReference `json:"parentRefs,omitempty"` - Labels map[string]string `json:"labels,omitempty"` -} - -type KuadrantOASRootExtension struct { - Route *RouteObject `json:"route,omitempty"` -} - -func NewKuadrantOASRootExtension(doc *openapi3.T) (*KuadrantOASRootExtension, error) { - type KuadrantOASRootObject struct { - // Kuadrant extension - Kuadrant *KuadrantOASRootExtension `json:"x-kuadrant,omitempty"` - } - - data, err := doc.MarshalJSON() - if err != nil { - return nil, err - } - - var x KuadrantOASRootObject - if err := json.Unmarshal(data, &x); err != nil { - return nil, err - } - - return x.Kuadrant, nil -} - -type KuadrantRateLimitExtension struct { - When []kuadrantapiv1beta2.WhenCondition `json:"when,omitempty"` - - Counters []kuadrantapiv1beta2.ContextSelector `json:"counters,omitempty"` - - Rates []kuadrantapiv1beta2.Rate `json:"rates,omitempty"` -} - -type KuadrantOASPathExtension struct { - Disable *bool `json:"disable,omitempty"` - PathMatchType *gatewayapiv1.PathMatchType `json:"pathMatchType,omitempty"` - BackendRefs []gatewayapiv1.HTTPBackendRef `json:"backendRefs,omitempty"` - RateLimit *KuadrantRateLimitExtension `json:"rate_limit,omitempty"` -} - -func (k *KuadrantOASPathExtension) IsDisabled() bool { - // Set default - return ptr.Deref(k.Disable, false) -} - -func (k *KuadrantOASPathExtension) GetPathMatchType() gatewayapiv1.PathMatchType { - // Set default - return ptr.Deref(k.PathMatchType, gatewayapiv1.PathMatchExact) -} - -func NewKuadrantOASPathExtension(pathItem *openapi3.PathItem) (*KuadrantOASPathExtension, error) { - type KuadrantOASPathObject struct { - // Kuadrant extension - Kuadrant *KuadrantOASPathExtension `json:"x-kuadrant,omitempty"` - } - - data, err := pathItem.MarshalJSON() - if err != nil { - return nil, err - } - - var x KuadrantOASPathObject - if err := json.Unmarshal(data, &x); err != nil { - return nil, err - } - - kuadrantExtension := ptr.Deref(x.Kuadrant, KuadrantOASPathExtension{}) - - return &kuadrantExtension, nil -} - -type KuadrantOASOperationExtension KuadrantOASPathExtension - -func NewKuadrantOASOperationExtension(operation *openapi3.Operation) (*KuadrantOASOperationExtension, error) { - type KuadrantOASOperationObject struct { - // Kuadrant extension - Kuadrant *KuadrantOASOperationExtension `json:"x-kuadrant,omitempty"` - } - - data, err := operation.MarshalJSON() - if err != nil { - return nil, err - } - - var x KuadrantOASOperationObject - if err := json.Unmarshal(data, &x); err != nil { - return nil, err - } - - kuadrantExtension := ptr.Deref(x.Kuadrant, KuadrantOASOperationExtension{}) - - return &kuadrantExtension, nil -} diff --git a/pkg/utils/maps.go b/pkg/utils/maps.go deleted file mode 100644 index c423dc4..0000000 --- a/pkg/utils/maps.go +++ /dev/null @@ -1,12 +0,0 @@ -package utils - -func MergeMaps[K comparable, V any](MyMap1 map[K]V, MyMap2 map[K]V) map[K]V { - merged := make(map[K]V) - for key, val := range MyMap1 { - merged[key] = val - } - for key, val := range MyMap2 { - merged[key] = val - } - return merged -} diff --git a/pkg/utils/oas_utils.go b/pkg/utils/oas_utils.go deleted file mode 100644 index a743470..0000000 --- a/pkg/utils/oas_utils.go +++ /dev/null @@ -1,193 +0,0 @@ -package utils - -import ( - "bytes" - "fmt" - "html/template" - "net/url" - "regexp" - - "github.com/getkin/kin-openapi/openapi3" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" -) - -var ( - // NonWordCharRegexp not word characters (== [^0-9A-Za-z_]) - NonWordCharRegexp = regexp.MustCompile(`\W`) - // TemplateRegexp used to render openapi server URLs - TemplateRegexp = regexp.MustCompile(`{([\w]+)}`) - // LastSlashRegexp matches the last slash - LastSlashRegexp = regexp.MustCompile(`/$`) -) - -func FirstServerFromOpenAPI(obj *openapi3.T) *openapi3.Server { - if obj == nil { - return nil - } - - // take only first server - // From https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md - // If the servers property is not provided, or is an empty array, the default value would be a Server Object with a url value of /. - server := &openapi3.Server{ - URL: `/`, - Variables: map[string]*openapi3.ServerVariable{}, - } - - // Current constraint: only read the first item when there are multiple servers - // Maybe this should be user provided setting - if len(obj.Servers) > 0 { - server = obj.Servers[0] - } - - return server -} - -func RenderOpenAPIServerURLStr(server *openapi3.Server) (string, error) { - if server == nil { - return "", nil - } - - data := &struct { - Data map[string]string - }{ - map[string]string{}, - } - - for variableName, variable := range server.Variables { - data.Data[variableName] = variable.Default - } - - urlTemplate := TemplateRegexp.ReplaceAllString(server.URL, `{{ index .Data "$1" }}`) - - tObj, err := template.New(server.URL).Parse(urlTemplate) - if err != nil { - return "", err - } - - var tpl bytes.Buffer - err = tObj.Execute(&tpl, data) - if err != nil { - return "", err - } - - return tpl.String(), nil -} - -func RenderOpenAPIServerURL(server *openapi3.Server) (*url.URL, error) { - serverURLStr, err := RenderOpenAPIServerURLStr(server) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(serverURLStr) - if err != nil { - return nil, err - } - - return serverURL, nil -} - -func BasePathFromOpenAPI(obj *openapi3.T) (string, error) { - server := FirstServerFromOpenAPI(obj) - serverURL, err := RenderOpenAPIServerURL(server) - if err != nil { - return "", err - } - - return serverURL.Path, nil -} - -func OpenAPIMatcherFromOASOperations(basePath, path string, pathItem *openapi3.PathItem, verb string, op *openapi3.Operation, pathMatchType gatewayapiv1.PathMatchType) gatewayapiv1.HTTPRouteMatch { - // remove the last slash of the Base Path - sanitizedBasePath := LastSlashRegexp.ReplaceAllString(basePath, "") - - // According OAS 3.0: path MUST begin with a slash - matchPath := fmt.Sprintf("%s%s", sanitizedBasePath, path) - - pathHeadersMatch := headersMatchFromParams(pathItem.Parameters) - operationHeadersMatch := headersMatchFromParams(op.Parameters) - - // default headersMatch at the path level - headersMatch := pathHeadersMatch - if len(operationHeadersMatch) > 0 { - headersMatch = operationHeadersMatch - } - - pathQueryParamsMatch := queryParamsMatchFromParams(pathItem.Parameters) - operationQueryParamsMatch := queryParamsMatchFromParams(op.Parameters) - - // default queryParams at the path level - queryParams := pathQueryParamsMatch - if len(operationQueryParamsMatch) > 0 { - queryParams = operationQueryParamsMatch - } - - return gatewayapiv1.HTTPRouteMatch{ - Method: &[]gatewayapiv1.HTTPMethod{gatewayapiv1.HTTPMethod(verb)}[0], - Path: &gatewayapiv1.HTTPPathMatch{ - Type: &pathMatchType, - Value: &[]string{matchPath}[0], - }, - Headers: headersMatch, - QueryParams: queryParams, - } -} - -func headersMatchFromParams(params openapi3.Parameters) []gatewayapiv1.HTTPHeaderMatch { - matches := make([]gatewayapiv1.HTTPHeaderMatch, 0) - - for _, parameter := range params { - if !parameter.Value.Required { - continue - } - - if parameter.Value.In == openapi3.ParameterInHeader { - matches = append(matches, gatewayapiv1.HTTPHeaderMatch{ - Type: &[]gatewayapiv1.HeaderMatchType{gatewayapiv1.HeaderMatchExact}[0], - Name: gatewayapiv1.HTTPHeaderName(parameter.Value.Name), - }) - } - } - - if len(matches) == 0 { - return nil - } - - return matches -} - -func queryParamsMatchFromParams(params openapi3.Parameters) []gatewayapiv1.HTTPQueryParamMatch { - matches := make([]gatewayapiv1.HTTPQueryParamMatch, 0) - - for _, parameter := range params { - if !parameter.Value.Required { - continue - } - - if parameter.Value.In == openapi3.ParameterInQuery { - matches = append(matches, gatewayapiv1.HTTPQueryParamMatch{ - Type: &[]gatewayapiv1.QueryParamMatchType{gatewayapiv1.QueryParamMatchExact}[0], - Name: gatewayapiv1.HTTPHeaderName(parameter.Value.Name), - }) - } - } - - if len(matches) == 0 { - return nil - } - - return matches - -} - -func OpenAPIOperationName(path, opVerb string, op *openapi3.Operation) string { - sanitizedPath := NonWordCharRegexp.ReplaceAllString(path, "") - - name := fmt.Sprintf("%s%s", opVerb, sanitizedPath) - - if op.OperationID != "" { - name = op.OperationID - } - - return name -} diff --git a/pkg/utils/suite_test.go b/pkg/utils/suite_test.go deleted file mode 100644 index e73724d..0000000 --- a/pkg/utils/suite_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package utils - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" -) - -func TestUtils(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Utils Suite") -} - -var _ = BeforeSuite(func() { - By("Before suite") - - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) -})