From dce0c27353dd7912e9e3e55e6f1b720530d63abb Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Wed, 5 Aug 2026 23:20:38 -0300 Subject: [PATCH 01/22] init test running successfully for 5.5 --- .../ccm-aws-tests/cmd/healthserver/Dockerfile | 10 + .../ccm-aws-tests/cmd/healthserver/main.go | 216 ++++++++++++ .../ccm-aws-tests/e2e/aws/health/client.go | 127 +++++++ .../ccm-aws-tests/e2e/aws/health/observer.go | 149 ++++++++ .../ccm-aws-tests/e2e/aws/health/types.go | 28 ++ .../e2e/aws/lb_health_transition.go | 326 ++++++++++++++++++ 6 files changed, 856 insertions(+) create mode 100644 openshift-tests/ccm-aws-tests/cmd/healthserver/Dockerfile create mode 100644 openshift-tests/ccm-aws-tests/cmd/healthserver/main.go create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/health/client.go create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/health/types.go create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go diff --git a/openshift-tests/ccm-aws-tests/cmd/healthserver/Dockerfile b/openshift-tests/ccm-aws-tests/cmd/healthserver/Dockerfile new file mode 100644 index 000000000..6483fb245 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/healthserver/Dockerfile @@ -0,0 +1,10 @@ +FROM golang:1.22-alpine AS builder +WORKDIR /build +COPY main.go . +RUN go mod init healthserver && \ + go mod edit -go=1.22 && \ + CGO_ENABLED=0 go build -ldflags="-s -w" -o healthserver . + +FROM scratch +COPY --from=builder /build/healthserver /healthserver +ENTRYPOINT ["/healthserver"] diff --git a/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go b/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go new file mode 100644 index 000000000..fee0890db --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go @@ -0,0 +1,216 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" +) + +type serverState string + +const ( + statePreReadyz serverState = "pre-readyz" + stateReady serverState = "ready" + stateDraining serverState = "draining" + stateShutdown serverState = "shutdown" +) + +type server struct { + mu sync.RWMutex + id string + state serverState + processStart time.Time + tcpUp time.Time + firstReadyz200 *time.Time + readyzFalseAt *time.Time + shutdownInitiated *time.Time +} + +func main() { + port := flag.Int("port", 8080, "Service port") + startupDelay := flag.Duration("startup-delay", 30*time.Second, "Duration before /readyz returns 200") + flag.Parse() + + id := os.Getenv("POD_NAME") + if id == "" { + id = fmt.Sprintf("healthserver-%d", os.Getpid()) + } + + s := &server{ + id: id, + state: statePreReadyz, + processStart: time.Now(), + } + + mux := http.NewServeMux() + mux.HandleFunc("GET /readyz", s.handleReadyz) + mux.HandleFunc("POST /admin/readyz", s.handleAdminReadyz) + mux.HandleFunc("POST /admin/shutdown", s.handleAdminShutdown) + mux.HandleFunc("GET /admin/lifecycle", s.handleAdminLifecycle) + mux.HandleFunc("/", s.handleMain) + + srv := &http.Server{ + Addr: fmt.Sprintf(":%d", *port), + Handler: mux, + } + + go func() { + log.Printf("startup delay: %s (readyz returns 503 until then)", *startupDelay) + time.Sleep(*startupDelay) + s.mu.Lock() + if s.state == statePreReadyz { + now := time.Now() + s.firstReadyz200 = &now + s.state = stateReady + log.Printf("startup delay elapsed, readyz now returns 200") + } + s.mu.Unlock() + }() + + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) + sig := <-ch + log.Printf("received %s, shutting down", sig) + s.mu.Lock() + now := time.Now() + s.shutdownInitiated = &now + s.state = stateShutdown + s.mu.Unlock() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + srv.Shutdown(ctx) + }() + + s.mu.Lock() + s.tcpUp = time.Now() + s.mu.Unlock() + + log.Printf("healthserver %s listening on :%d (startup-delay=%s)", id, *port, *startupDelay) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %v", err) + } +} + +func (s *server) handleMain(w http.ResponseWriter, r *http.Request) { + s.mu.RLock() + state := s.state + start := s.processStart + var firstReadyz string + if s.firstReadyz200 != nil { + firstReadyz = s.firstReadyz200.Format(time.RFC3339Nano) + } else { + firstReadyz = "never" + } + s.mu.RUnlock() + + w.Header().Set("X-Server-State", string(state)) + w.Header().Set("X-Server-ID", s.id) + w.Header().Set("X-Server-Start-Time", start.Format(time.RFC3339Nano)) + w.Header().Set("X-First-Readyz-Time", firstReadyz) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "server_id=%s state=%s\n", s.id, state) +} + +func (s *server) handleReadyz(w http.ResponseWriter, r *http.Request) { + s.mu.RLock() + state := s.state + s.mu.RUnlock() + + if state == stateReady { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "ok") + } else { + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintf(w, "not ready: %s", state) + } +} + +func (s *server) handleAdminReadyz(w http.ResponseWriter, r *http.Request) { + ready := r.URL.Query().Get("ready") + s.mu.Lock() + defer s.mu.Unlock() + + switch ready { + case "true": + if s.firstReadyz200 == nil { + now := time.Now() + s.firstReadyz200 = &now + } + s.state = stateReady + fmt.Fprint(w, "readyz=true") + case "false": + now := time.Now() + s.readyzFalseAt = &now + s.state = stateDraining + fmt.Fprint(w, "readyz=false") + default: + http.Error(w, "ready must be 'true' or 'false'", http.StatusBadRequest) + } +} + +func (s *server) handleAdminShutdown(w http.ResponseWriter, r *http.Request) { + delayStr := r.URL.Query().Get("delay") + delay := time.Duration(0) + if delayStr != "" { + d, err := time.ParseDuration(delayStr) + if err != nil { + http.Error(w, fmt.Sprintf("invalid delay: %v", err), http.StatusBadRequest) + return + } + delay = d + } + + s.mu.Lock() + now := time.Now() + s.shutdownInitiated = &now + s.state = stateShutdown + s.mu.Unlock() + + fmt.Fprintf(w, "shutdown initiated, delay=%s", delay) + + go func() { + time.Sleep(delay) + os.Exit(0) + }() +} + +func (s *server) handleAdminLifecycle(w http.ResponseWriter, r *http.Request) { + s.mu.RLock() + j := struct { + ServerID string `json:"server_id"` + ProcessStart string `json:"t_process_start"` + TCPUp string `json:"t_tcp_up"` + FirstReadyz200 *string `json:"t_first_readyz_200"` + ReadyzFalseAt *string `json:"t_readyz_false_at"` + ShutdownInitiated *string `json:"t_shutdown_initiated"` + }{ + ServerID: s.id, + ProcessStart: s.processStart.Format(time.RFC3339Nano), + TCPUp: s.tcpUp.Format(time.RFC3339Nano), + } + if s.firstReadyz200 != nil { + v := s.firstReadyz200.Format(time.RFC3339Nano) + j.FirstReadyz200 = &v + } + if s.readyzFalseAt != nil { + v := s.readyzFalseAt.Format(time.RFC3339Nano) + j.ReadyzFalseAt = &v + } + if s.shutdownInitiated != nil { + v := s.shutdownInitiated.Format(time.RFC3339Nano) + j.ShutdownInitiated = &v + } + s.mu.RUnlock() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(j) +} diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go new file mode 100644 index 000000000..640b94f61 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go @@ -0,0 +1,127 @@ +package health + +import ( + "context" + "crypto/tls" + "fmt" + "io" + "net" + "net/http" + "net/http/httptrace" + "sync" + "time" +) + +// Client sends HTTP requests through the NLB at a configurable interval, +// capturing per-request connection timing and server-reported state headers. +// Each request uses a new TCP connection (DisableKeepAlives) to match NLB +// per-connection routing behavior. +type Client struct { + targetURL string + interval time.Duration + httpClient *http.Client + + mu sync.Mutex + records []RequestRecord + + cancel context.CancelFunc +} + +// NewClient creates a Client that polls the given URL at the given interval. +func NewClient(targetURL string, interval time.Duration) *Client { + return &Client{ + targetURL: targetURL, + interval: interval, + httpClient: &http.Client{ + Transport: &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + Timeout: 10 * time.Second, + }, + } +} + +// Start begins sending requests in a background goroutine. +func (c *Client) Start(ctx context.Context) { + ctx, c.cancel = context.WithCancel(ctx) + go c.pollLoop(ctx) +} + +// Stop cancels the background request goroutine. +func (c *Client) Stop() { + if c.cancel != nil { + c.cancel() + } +} + +// Records returns a copy of all captured request records. +func (c *Client) Records() []RequestRecord { + c.mu.Lock() + defer c.mu.Unlock() + result := make([]RequestRecord, len(c.records)) + copy(result, c.records) + return result +} + +func (c *Client) pollLoop(ctx context.Context) { + ticker := time.NewTicker(c.interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + c.doRequest(ctx) + } + } +} + +func (c *Client) doRequest(ctx context.Context) { + rec := RequestRecord{Timestamp: time.Now()} + var dialStart time.Time + + trace := &httptrace.ClientTrace{ + ConnectStart: func(network, addr string) { + dialStart = time.Now() + if host, _, err := net.SplitHostPort(addr); err == nil { + rec.TargetIP = host + } + }, + ConnectDone: func(network, addr string, err error) { + if err == nil { + rec.TCPDialDuration = time.Since(dialStart) + } + }, + } + + req, err := http.NewRequestWithContext(httptrace.WithClientTrace(ctx, trace), "GET", c.targetURL, nil) + if err != nil { + rec.Error = fmt.Sprintf("create request: %v", err) + c.addRecord(rec) + return + } + + resp, err := c.httpClient.Do(req) + if err != nil { + rec.Error = fmt.Sprintf("request: %v", err) + c.addRecord(rec) + return + } + defer resp.Body.Close() + io.Copy(io.Discard, resp.Body) + + rec.HTTPStatus = resp.StatusCode + rec.ServerState = resp.Header.Get("X-Server-State") + rec.ServerID = resp.Header.Get("X-Server-ID") + rec.FirstReadyzTime = resp.Header.Get("X-First-Readyz-Time") + rec.IsNonReadyReq = rec.ServerState == "pre-readyz" + + c.addRecord(rec) +} + +func (c *Client) addRecord(rec RequestRecord) { + c.mu.Lock() + c.records = append(c.records, rec) + c.mu.Unlock() +} diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go new file mode 100644 index 000000000..dad775ee0 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go @@ -0,0 +1,149 @@ +package health + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" + elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" + "k8s.io/apimachinery/pkg/util/wait" +) + +// Observer polls the AWS DescribeTargetHealth API at a configurable interval, +// recording state transitions per target. It discovers the target group ARN +// from the load balancer ARN. +type Observer struct { + elbClient *elbv2.Client + tgARN string + targetType string + interval time.Duration + + mu sync.Mutex + events []HealthEvent + lastState map[string]string + + cancel context.CancelFunc +} + +// NewObserver creates an Observer that polls target health at the given interval. +func NewObserver(elbClient *elbv2.Client, interval time.Duration) *Observer { + return &Observer{ + elbClient: elbClient, + interval: interval, + lastState: make(map[string]string), + } +} + +// DiscoverTargetGroup finds the first target group associated with the given +// NLB ARN and records its ARN and target type. +func (o *Observer) DiscoverTargetGroup(ctx context.Context, lbARN string) error { + output, err := o.elbClient.DescribeTargetGroups(ctx, &elbv2.DescribeTargetGroupsInput{ + LoadBalancerArn: aws.String(lbARN), + }) + if err != nil { + return fmt.Errorf("describe target groups: %w", err) + } + if len(output.TargetGroups) == 0 { + return fmt.Errorf("no target groups for LB %s", lbARN) + } + o.tgARN = aws.ToString(output.TargetGroups[0].TargetGroupArn) + o.targetType = string(output.TargetGroups[0].TargetType) + return nil +} + +// TargetGroupARN returns the discovered target group ARN. +func (o *Observer) TargetGroupARN() string { return o.tgARN } + +// TargetType returns the target type (instance, ip, lambda, alb). +func (o *Observer) TargetType() string { return o.targetType } + +// WaitForAllHealthy blocks until at least minHealthy targets report +// TargetHealthStateEnumHealthy, or the timeout is reached. +func (o *Observer) WaitForAllHealthy(ctx context.Context, minHealthy int, timeout time.Duration) error { + return wait.PollUntilContextTimeout(ctx, o.interval, timeout, true, func(ctx context.Context) (bool, error) { + output, err := o.elbClient.DescribeTargetHealth(ctx, &elbv2.DescribeTargetHealthInput{ + TargetGroupArn: aws.String(o.tgARN), + }) + if err != nil { + return false, nil + } + healthy := 0 + for _, d := range output.TargetHealthDescriptions { + if d.TargetHealth.State == elbv2types.TargetHealthStateEnumHealthy { + healthy++ + } + } + return healthy >= minHealthy, nil + }) +} + +// Start begins polling DescribeTargetHealth in a background goroutine. +func (o *Observer) Start(ctx context.Context) { + ctx, o.cancel = context.WithCancel(ctx) + go o.pollLoop(ctx) +} + +// Stop cancels the background polling goroutine. +func (o *Observer) Stop() { + if o.cancel != nil { + o.cancel() + } +} + +// Events returns a copy of all recorded health state transition events. +func (o *Observer) Events() []HealthEvent { + o.mu.Lock() + defer o.mu.Unlock() + result := make([]HealthEvent, len(o.events)) + copy(result, o.events) + return result +} + +func (o *Observer) pollLoop(ctx context.Context) { + ticker := time.NewTicker(o.interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + o.pollOnce(ctx) + } + } +} + +func (o *Observer) pollOnce(ctx context.Context) { + output, err := o.elbClient.DescribeTargetHealth(ctx, &elbv2.DescribeTargetHealthInput{ + TargetGroupArn: aws.String(o.tgARN), + }) + if err != nil { + return + } + + o.mu.Lock() + defer o.mu.Unlock() + + now := time.Now() + for _, d := range output.TargetHealthDescriptions { + id := aws.ToString(d.Target.Id) + port := aws.ToInt32(d.Target.Port) + state := string(d.TargetHealth.State) + reason := string(d.TargetHealth.Reason) + + prev := o.lastState[id] + if state != prev { + o.events = append(o.events, HealthEvent{ + Timestamp: now, + TargetID: id, + TargetPort: port, + State: state, + PrevState: prev, + Reason: reason, + }) + o.lastState[id] = state + } + } +} diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go new file mode 100644 index 000000000..f67802d64 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go @@ -0,0 +1,28 @@ +package health + +import "time" + +// HealthEvent records a target health state transition observed from the +// AWS DescribeTargetHealth API. +type HealthEvent struct { + Timestamp time.Time + TargetID string + TargetPort int32 + State string + PrevState string + Reason string +} + +// RequestRecord captures a single HTTP request through the NLB, including +// connection-level timing from httptrace and server-reported state headers. +type RequestRecord struct { + Timestamp time.Time + TargetIP string + TCPDialDuration time.Duration + HTTPStatus int + ServerState string + ServerID string + FirstReadyzTime string + IsNonReadyReq bool + Error string +} diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go new file mode 100644 index 000000000..b0e672683 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -0,0 +1,326 @@ +package aws + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/aws/health" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/wait" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" + admissionapi "k8s.io/pod-security-admission/api" +) + +const ( + envHealthserverImage = "HEALTHSERVER_IMAGE" + + healthTransitionTestPrefix = e2eTestPrefixLoadBalancer + " health-transition" +) + +// Scenario 5.5: Pre-readyz routing detection (OCPBUGS-86789 reproducer). +// +// Deploys a health-controllable server behind an NLB, triggers a pod restart, +// and checks whether the NLB routes NEW connections to the restarted target +// before its /readyz endpoint returns 200 — while other healthy targets exist. +// +// Prerequisites: +// - HEALTHSERVER_IMAGE environment variable set to a registry-accessible +// healthserver container image (built from cmd/healthserver/). +// - AWS credentials with ELBv2 DescribeTargetHealth and DescribeTargetGroups. +// +// How to run: +// +// export HEALTHSERVER_IMAGE=quay.io//healthserver:latest +// ./openshift-tests/bin/cloud-controller-manager-aws-tests-ext run-test \ +// "[cloud-provider-aws-e2e-openshift] loadbalancer health-transition NLB target health state transitions during rolling restart should not route to pre-readyz targets when healthy targets are available [Suite:openshift/conformance/parallel]" +var _ = Describe(healthTransitionTestPrefix+" NLB", func() { + f := framework.NewDefaultFramework("cloud-provider-aws") + f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged + + var cs clientset.Interface + var ns *v1.Namespace + + BeforeEach(func() { + cs = f.ClientSet + ns = f.Namespace + }) + + Context("target health state transitions during rolling restart", func() { + It("should not route to pre-readyz targets "+ + "when healthy targets are available", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set, skipping health transition test", envHealthserverImage)) + } + + replicas := int32(3) + startupDelay := 60 * time.Second + clientInterval := 500 * time.Millisecond + observerInterval := 1 * time.Second + steadyStateDuration := 30 * time.Second + observeDuration := startupDelay + 3*time.Minute + + deployName := "healthserver" + svcName := "healthserver-lb" + + By("creating healthserver Deployment") + deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image) + _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create deployment") + + By("creating NLB Service with /readyz health check") + svc := buildHealthTransitionService(ns.Name, svcName, deployName) + _, err = cs.CoreV1().Services(ns.Name).Create(ctx, svc, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create service") + + var lbDNS string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up health transition test resources") + _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, svcName, metav1.DeleteOptions{}) + if lbDNS != "" { + waitForLBDeletion(cleanupCtx, lbDNS) + } + }) + + By("waiting for Deployment rollout") + err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + d, err := cs.AppsV1().Deployments(ns.Name).Get(ctx, deployName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + framework.Logf("deployment ready replicas: %d/%d", d.Status.ReadyReplicas, replicas) + return d.Status.ReadyReplicas >= replicas, nil + }) + framework.ExpectNoError(err, "deployment rollout") + + By("waiting for NLB provisioning") + err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + s, err := cs.CoreV1().Services(ns.Name).Get(ctx, svcName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + if len(s.Status.LoadBalancer.Ingress) > 0 { + lbDNS = s.Status.LoadBalancer.Ingress[0].Hostname + return lbDNS != "", nil + } + return false, nil + }) + framework.ExpectNoError(err, "NLB provisioning") + framework.Logf("NLB DNS: %s", lbDNS) + + By("discovering NLB and target group in AWS") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + foundLB, err := getAWSLoadBalancerFromDNSName(ctx, elbClient, lbDNS) + framework.ExpectNoError(err, "find NLB") + lbARN := aws.ToString(foundLB.LoadBalancerArn) + framework.Logf("NLB ARN: %s", lbARN) + + observer := health.NewObserver(elbClient, observerInterval) + err = observer.DiscoverTargetGroup(ctx, lbARN) + framework.ExpectNoError(err, "discover target group") + framework.Logf("TG ARN: %s (target type: %s)", observer.TargetGroupARN(), observer.TargetType()) + + By("waiting for all TG targets to become healthy") + err = observer.WaitForAllHealthy(ctx, int(replicas), 10*time.Minute) + framework.ExpectNoError(err, "targets healthy") + framework.Logf("all %d TG targets are healthy", replicas) + + By("starting client polling and TG health observer") + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), clientInterval) + observer.Start(ctx) + client.Start(ctx) + + By(fmt.Sprintf("verifying steady state for %s", steadyStateDuration)) + time.Sleep(steadyStateDuration) + + steadyRecords := client.Records() + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + framework.Logf("steady state: %d requests, %d non-ready", len(steadyRecords), steadyNonReady) + Expect(steadyNonReady).To(Equal(0), "no pre-readyz responses expected during steady state") + + By("deleting one pod to trigger restart cycle") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + framework.Logf("deleting pod %s (node: %s) to trigger restart", targetPod, targetNode) + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err, "delete pod") + + By(fmt.Sprintf("observing health transitions for %s", observeDuration)) + time.Sleep(observeDuration) + + By("collecting results") + client.Stop() + observer.Stop() + + allRecords := client.Records() + allEvents := observer.Events() + + nonReadyCount := 0 + var nonReadyDetails []string + uniqueServers := make(map[string]bool) + errorCount := 0 + + for _, r := range allRecords { + if r.ServerID != "" { + uniqueServers[r.ServerID] = true + } + if r.IsNonReadyReq { + nonReadyCount++ + nonReadyDetails = append(nonReadyDetails, fmt.Sprintf( + " t=%s server=%s target_ip=%s tcp_dial=%s", + r.Timestamp.Format(time.RFC3339), r.ServerID, r.TargetIP, r.TCPDialDuration)) + } + if r.Error != "" { + errorCount++ + } + } + + framework.Logf("") + framework.Logf("═══════════════════════════════════════════════════════════") + framework.Logf("HEALTH TRANSITION REPORT — Scenario 5.5 (Pre-Readyz Routing)") + framework.Logf("═══════════════════════════════════════════════════════════") + framework.Logf("Replicas: %d", replicas) + framework.Logf("Startup Delay: %s", startupDelay) + framework.Logf("Total Requests: %d", len(allRecords)) + framework.Logf("Request Errors: %d", errorCount) + framework.Logf("Unique Servers: %d", len(uniqueServers)) + framework.Logf("NonReadyRequests: %d", nonReadyCount) + framework.Logf("TG Health Events: %d", len(allEvents)) + + if nonReadyCount > 0 { + framework.Logf("") + framework.Logf("PRE-READYZ ROUTING DETECTED (OCPBUGS-86789):") + for _, d := range nonReadyDetails { + framework.Logf("%s", d) + } + } + + framework.Logf("") + framework.Logf("TG Health Timeline:") + for _, e := range allEvents { + framework.Logf(" t=%s target=%s %s→%s reason=%s", + e.Timestamp.Format(time.RFC3339), e.TargetID, e.PrevState, e.State, e.Reason) + } + + framework.Logf("") + if nonReadyCount > 0 { + framework.Logf("VERDICT: NLB routed %d request(s) to pre-readyz target(s)", nonReadyCount) + framework.Logf("This reproduces OCPBUGS-86789 — NLB routes before /readyz passes") + } else { + framework.Logf("VERDICT: No pre-readyz routing detected in this iteration") + } + framework.Logf("═══════════════════════════════════════════════════════════") + }) + }) +}) + +func buildHealthserverDeployment(namespace, name string, replicas int32, startupDelay time.Duration, image string) *appsv1.Deployment { + labels := map[string]string{"app": name} + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{Labels: labels}, + Spec: v1.PodSpec{ + TopologySpreadConstraints: []v1.TopologySpreadConstraint{{ + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: v1.ScheduleAnyway, + LabelSelector: &metav1.LabelSelector{MatchLabels: labels}, + }}, + Containers: []v1.Container{{ + Name: "healthserver", + Image: image, + Args: []string{fmt.Sprintf("--startup-delay=%s", startupDelay)}, + Ports: []v1.ContainerPort{{ + Name: "http", + ContainerPort: 8080, + }}, + Env: []v1.EnvVar{{ + Name: "POD_NAME", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.name"}, + }, + }}, + }}, + }, + }, + }, + } +} + +func buildHealthTransitionService(namespace, name, deployName string) *v1.Service { + return &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Annotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol": "HTTP", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-path": "/readyz", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": "traffic-port", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval": "10", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold": "2", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold": "2", + }, + }, + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, + Selector: map[string]string{"app": deployName}, + Ports: []v1.ServicePort{{ + Name: "http", + Protocol: v1.ProtocolTCP, + Port: 80, + TargetPort: intstr.FromInt(8080), + }}, + }, + } +} + +func waitForLBDeletion(ctx context.Context, lbDNS string) { + elbClient, err := createAWSClientLoadBalancer(ctx) + if err != nil { + framework.Logf("failed to create ELB client for cleanup: %v", err) + return + } + err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + lb, err := findAWSLoadBalancerByDNSName(ctx, elbClient, lbDNS) + if err != nil { + return false, nil + } + return lb == nil, nil + }) + if err != nil { + framework.Logf("warning: timed out waiting for LB deletion: %v", err) + } +} From 14069d6d9723be01a8bdcf55c7f14119e2218fcd Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 00:45:37 -0300 Subject: [PATCH 02/22] health: add per-poll TG snapshots and TG attribute introspection Extend the health observer to capture full TG state on every poll (TargetSnapshot with healthy/unhealthy/initial/draining counts and per-target state map), matching the SPLAT-307 CSV format for consistent cross-run comparison. Add DescribeTGAttributes() to fetch TG configuration (connection termination, draining interval, etc.) for inclusion in test reports. Add TGAttribute type to the observer package so callers can read the TG config without importing the AWS SDK directly. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/observer.go | 57 ++++++++++++++++++- .../ccm-aws-tests/e2e/aws/health/types.go | 11 ++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go index dad775ee0..1e541d6a1 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go @@ -13,8 +13,7 @@ import ( ) // Observer polls the AWS DescribeTargetHealth API at a configurable interval, -// recording state transitions per target. It discovers the target group ARN -// from the load balancer ARN. +// recording state transitions per target and full-state snapshots per poll. type Observer struct { elbClient *elbv2.Client tgARN string @@ -23,6 +22,7 @@ type Observer struct { mu sync.Mutex events []HealthEvent + snapshots []TargetSnapshot lastState map[string]string cancel context.CancelFunc @@ -60,6 +60,30 @@ func (o *Observer) TargetGroupARN() string { return o.tgARN } // TargetType returns the target type (instance, ip, lambda, alb). func (o *Observer) TargetType() string { return o.targetType } +// TGAttribute is a key-value pair from DescribeTargetGroupAttributes. +type TGAttribute struct { + Key string + Value string +} + +// DescribeTGAttributes returns the target group attributes for the discovered TG. +func (o *Observer) DescribeTGAttributes(ctx context.Context) ([]TGAttribute, error) { + output, err := o.elbClient.DescribeTargetGroupAttributes(ctx, &elbv2.DescribeTargetGroupAttributesInput{ + TargetGroupArn: aws.String(o.tgARN), + }) + if err != nil { + return nil, fmt.Errorf("describe TG attributes: %w", err) + } + attrs := make([]TGAttribute, 0, len(output.Attributes)) + for _, a := range output.Attributes { + attrs = append(attrs, TGAttribute{ + Key: aws.ToString(a.Key), + Value: aws.ToString(a.Value), + }) + } + return attrs, nil +} + // WaitForAllHealthy blocks until at least minHealthy targets report // TargetHealthStateEnumHealthy, or the timeout is reached. func (o *Observer) WaitForAllHealthy(ctx context.Context, minHealthy int, timeout time.Duration) error { @@ -102,6 +126,15 @@ func (o *Observer) Events() []HealthEvent { return result } +// Snapshots returns a copy of all per-poll full-state snapshots. +func (o *Observer) Snapshots() []TargetSnapshot { + o.mu.Lock() + defer o.mu.Unlock() + result := make([]TargetSnapshot, len(o.snapshots)) + copy(result, o.snapshots) + return result +} + func (o *Observer) pollLoop(ctx context.Context) { ticker := time.NewTicker(o.interval) defer ticker.Stop() @@ -127,12 +160,30 @@ func (o *Observer) pollOnce(ctx context.Context) { defer o.mu.Unlock() now := time.Now() + + snap := TargetSnapshot{ + Timestamp: now, + Targets: make(map[string]string, len(output.TargetHealthDescriptions)), + } + for _, d := range output.TargetHealthDescriptions { id := aws.ToString(d.Target.Id) port := aws.ToInt32(d.Target.Port) state := string(d.TargetHealth.State) reason := string(d.TargetHealth.Reason) + snap.Targets[id] = state + switch d.TargetHealth.State { + case elbv2types.TargetHealthStateEnumHealthy: + snap.HealthyCount++ + case elbv2types.TargetHealthStateEnumUnhealthy, elbv2types.TargetHealthStateEnumUnhealthyDraining: + snap.UnhealthyCount++ + case elbv2types.TargetHealthStateEnumInitial: + snap.InitialCount++ + case elbv2types.TargetHealthStateEnumDraining: + snap.DrainingCount++ + } + prev := o.lastState[id] if state != prev { o.events = append(o.events, HealthEvent{ @@ -146,4 +197,6 @@ func (o *Observer) pollOnce(ctx context.Context) { o.lastState[id] = state } } + + o.snapshots = append(o.snapshots, snap) } diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go index f67802d64..ed21ff5dc 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/types.go @@ -26,3 +26,14 @@ type RequestRecord struct { IsNonReadyReq bool Error string } + +// TargetSnapshot captures the full TG health state at a single poll instant. +// Used for per-second timeline output matching the SPLAT-307 CSV format. +type TargetSnapshot struct { + Timestamp time.Time + HealthyCount int + UnhealthyCount int + InitialCount int + DrainingCount int + Targets map[string]string // targetID -> health state +} From 9f6750a175a85fc43a12e786a111be3b3fad5b56 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 00:46:50 -0300 Subject: [PATCH 03/22] e2e: add graceful shutdown, Scenario 5.2, consistent timing model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major enhancement of the NLB health transition e2e tests based on feedback from first test runs and alignment with SPLAT-307 research. Graceful shutdown simulation: - Signal readyz→503 via K8s API server pod proxy before deleting pod - Configurable shutdown-delay (192s, matching KAS shutdown-delay-duration) - Pod keeps serving with X-Server-State: draining during shutdown window Scenario 5.2 (SPLAT-307 revalidation): - Shutdown propagation measurement without pod restart - Signals readyz→503, observes propagation, signals readyz→200, observes recovery. Measures T_route_stop and T_route_start independently. Consistent timing model (t5→t10): - Every test reports the same set of timers regardless of scenario - New restart-phase timers t7.1 (pod deleted), t7.3 (new TCP up), t7.4 (pre-readyz request = BUG) - Computed metrics map directly to SPLAT-307 data table rows Report improvements: - Single-block output (all lines in one framework.Logf call, no per-line logger timestamps) - Service annotations and TG attributes/health check config in report - Unified chronological timeline merging test milestones with TG events, full RFC3339 timestamps Bug fixes from first 3 test runs: - knownServers built from pod list, not client records (which may miss pods due to NLB routing distribution during 30s steady state) - t8 timezone: apply .Local() after parsing UTC X-First-Readyz-Time - t9 anchored to t7.1 (pod deletion), not t8 (which could be stale) - t6 filter requires healthy→unhealthy (excludes initial→unhealthy from nodes without local pods) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../e2e/aws/lb_health_transition.go | 785 ++++++++++++++---- 1 file changed, 637 insertions(+), 148 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index b0e672683..e9453bef5 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -4,9 +4,12 @@ import ( "context" "fmt" "os" + "sort" + "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" + elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/aws/health" @@ -24,24 +27,53 @@ const ( envHealthserverImage = "HEALTHSERVER_IMAGE" healthTransitionTestPrefix = e2eTestPrefixLoadBalancer + " health-transition" + + // kasShutdownDelay matches the KAS shutdown-delay-duration (135s graceful + + // margin), simulating how long KAS keeps serving after /readyz→503 before + // the process exits. CKAO sets 135s; we add buffer for HC propagation. + kasShutdownDelay = 192 * time.Second ) -// Scenario 5.5: Pre-readyz routing detection (OCPBUGS-86789 reproducer). -// -// Deploys a health-controllable server behind an NLB, triggers a pod restart, -// and checks whether the NLB routes NEW connections to the restarted target -// before its /readyz endpoint returns 200 — while other healthy targets exist. -// -// Prerequisites: -// - HEALTHSERVER_IMAGE environment variable set to a registry-accessible -// healthserver container image (built from cmd/healthserver/). -// - AWS credentials with ELBv2 DescribeTargetHealth and DescribeTargetGroups. -// -// How to run: +// transitionTimeline captures all timing milestones from the SPLAT-307 state +// machine extended with restart-phase timers (t7.1–t7.4) for OCPBUGS-86789. // -// export HEALTHSERVER_IMAGE=quay.io//healthserver:latest -// ./openshift-tests/bin/cloud-controller-manager-aws-tests-ext run-test \ -// "[cloud-provider-aws-e2e-openshift] loadbalancer health-transition NLB target health state transitions during rolling restart should not route to pre-readyz targets when healthy targets are available [Suite:openshift/conformance/parallel]" +// A zero time.Time means the milestone was not observed. +type transitionTimeline struct { + // Shutdown phase (SPLAT-307 path) + T5 time.Time // readyz→503 signal sent + T6 time.Time // first observer event: target unhealthy + T7 time.Time // last client request routed to target after t5 + + // Restart phase (Scenario 5.5 only; zero for 5.2) + T71 time.Time // pod delete sent + T73 time.Time // new pod TCP up (from X-Server-Start-Time header) + T74 time.Time // first pre-readyz request from new pod (BUG if present) + + // Startup phase + T8 time.Time // readyz→200 (from X-First-Readyz-Time header or admin signal) + T9 time.Time // first observer event: target healthy after t8 + T10 time.Time // first client request to target after t9 + + // Counters + UnhealthyReqCount int // requests served by target between t5 and t7 + PreReadyzReqCount int // requests with X-Server-State: pre-readyz + + // Identity + TargetPod string + TargetNode string + NewPod string +} + +// serviceConfig records Service and TG configuration for the report. +type serviceConfig struct { + ServiceAnnotations map[string]string + TGAttributes []health.TGAttribute + TGARN string + TGTargetType string + LBARN string + LBDNS string +} + var _ = Describe(healthTransitionTestPrefix+" NLB", func() { f := framework.NewDefaultFramework("cloud-provider-aws") f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged @@ -54,94 +86,35 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { ns = f.Namespace }) - Context("target health state transitions during rolling restart", func() { + // ── Scenario 5.5 ─────────────────────────────────────────────────── + Context("pre-readyz routing detection (OCPBUGS-86789)", func() { It("should not route to pre-readyz targets "+ "when healthy targets are available", func(ctx context.Context) { image := os.Getenv(envHealthserverImage) if image == "" { - Skip(fmt.Sprintf("%s not set, skipping health transition test", envHealthserverImage)) + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) } replicas := int32(3) startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay clientInterval := 500 * time.Millisecond observerInterval := 1 * time.Second steadyStateDuration := 30 * time.Second - observeDuration := startupDelay + 3*time.Minute deployName := "healthserver" svcName := "healthserver-lb" - By("creating healthserver Deployment") - deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image) - _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) - framework.ExpectNoError(err, "create deployment") - - By("creating NLB Service with /readyz health check") - svc := buildHealthTransitionService(ns.Name, svcName, deployName) - _, err = cs.CoreV1().Services(ns.Name).Create(ctx, svc, metav1.CreateOptions{}) - framework.ExpectNoError(err, "create service") - - var lbDNS string - DeferCleanup(func(cleanupCtx context.Context) { - framework.Logf("cleaning up health transition test resources") - _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) - _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, svcName, metav1.DeleteOptions{}) - if lbDNS != "" { - waitForLBDeletion(cleanupCtx, lbDNS) - } - }) - - By("waiting for Deployment rollout") - err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { - d, err := cs.AppsV1().Deployments(ns.Name).Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - return false, nil - } - framework.Logf("deployment ready replicas: %d/%d", d.Status.ReadyReplicas, replicas) - return d.Status.ReadyReplicas >= replicas, nil - }) - framework.ExpectNoError(err, "deployment rollout") + lbDNS, observer, svcCfg := setupHealthTransition( + ctx, cs, ns, deployName, svcName, image, + replicas, startupDelay, observerInterval, + ) - By("waiting for NLB provisioning") - err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { - s, err := cs.CoreV1().Services(ns.Name).Get(ctx, svcName, metav1.GetOptions{}) - if err != nil { - return false, nil - } - if len(s.Status.LoadBalancer.Ingress) > 0 { - lbDNS = s.Status.LoadBalancer.Ingress[0].Hostname - return lbDNS != "", nil - } - return false, nil - }) - framework.ExpectNoError(err, "NLB provisioning") - framework.Logf("NLB DNS: %s", lbDNS) - - By("discovering NLB and target group in AWS") - elbClient, err := createAWSClientLoadBalancer(ctx) - framework.ExpectNoError(err, "create ELB client") - - foundLB, err := getAWSLoadBalancerFromDNSName(ctx, elbClient, lbDNS) - framework.ExpectNoError(err, "find NLB") - lbARN := aws.ToString(foundLB.LoadBalancerArn) - framework.Logf("NLB ARN: %s", lbARN) - - observer := health.NewObserver(elbClient, observerInterval) - err = observer.DiscoverTargetGroup(ctx, lbARN) - framework.ExpectNoError(err, "discover target group") - framework.Logf("TG ARN: %s (target type: %s)", observer.TargetGroupARN(), observer.TargetType()) - - By("waiting for all TG targets to become healthy") - err = observer.WaitForAllHealthy(ctx, int(replicas), 10*time.Minute) - framework.ExpectNoError(err, "targets healthy") - framework.Logf("all %d TG targets are healthy", replicas) - - By("starting client polling and TG health observer") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), clientInterval) observer.Start(ctx) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), clientInterval) client.Start(ctx) + defer func() { client.Stop(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", steadyStateDuration)) time.Sleep(steadyStateDuration) @@ -153,91 +126,611 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { steadyNonReady++ } } - framework.Logf("steady state: %d requests, %d non-ready", len(steadyRecords), steadyNonReady) - Expect(steadyNonReady).To(Equal(0), "no pre-readyz responses expected during steady state") + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") - By("deleting one pod to trigger restart cycle") pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployName), }) - framework.ExpectNoError(err, "list pods") + framework.ExpectNoError(err) Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + // Build knownServers from ALL existing pods (not client records, + // which may miss pods due to NLB routing distribution). + knownServers := make(map[string]bool) + for _, p := range pods.Items { + knownServers[p.Name] = true + } + targetPod := pods.Items[0].Name targetNode := pods.Items[0].Spec.NodeName - framework.Logf("deleting pod %s (node: %s) to trigger restart", targetPod, targetNode) + + By("signaling target pod readyz→503 (t5)") + t5 := time.Now() + err = sendAdminSignal(ctx, cs, ns.Name, targetPod, false) + framework.ExpectNoError(err, "signal readyz→false") + + By(fmt.Sprintf("waiting %s shutdown-delay before pod deletion", shutdownDelay)) + time.Sleep(shutdownDelay) + + By("deleting target pod (t7.1)") + t71 := time.Now() err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) - framework.ExpectNoError(err, "delete pod") + framework.ExpectNoError(err) - By(fmt.Sprintf("observing health transitions for %s", observeDuration)) - time.Sleep(observeDuration) + By("waiting for replacement pod") + newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) - By("collecting results") - client.Stop() - observer.Stop() + observeDuration := startupDelay + 3*time.Minute + By(fmt.Sprintf("observing for %s (startup-delay + propagation buffer)", observeDuration)) + time.Sleep(observeDuration) allRecords := client.Records() allEvents := observer.Events() - nonReadyCount := 0 - var nonReadyDetails []string - uniqueServers := make(map[string]bool) - errorCount := 0 + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod - for _, r := range allRecords { - if r.ServerID != "" { - uniqueServers[r.ServerID] = true - } - if r.IsNonReadyReq { - nonReadyCount++ - nonReadyDetails = append(nonReadyDetails, fmt.Sprintf( - " t=%s server=%s target_ip=%s tcp_dial=%s", - r.Timestamp.Format(time.RFC3339), r.ServerID, r.TargetIP, r.TCPDialDuration)) - } - if r.Error != "" { - errorCount++ - } - } + report := buildReport("5.5 (Pre-Readyz Routing / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allEvents, observer.Snapshots()) - framework.Logf("") - framework.Logf("═══════════════════════════════════════════════════════════") - framework.Logf("HEALTH TRANSITION REPORT — Scenario 5.5 (Pre-Readyz Routing)") - framework.Logf("═══════════════════════════════════════════════════════════") - framework.Logf("Replicas: %d", replicas) - framework.Logf("Startup Delay: %s", startupDelay) - framework.Logf("Total Requests: %d", len(allRecords)) - framework.Logf("Request Errors: %d", errorCount) - framework.Logf("Unique Servers: %d", len(uniqueServers)) - framework.Logf("NonReadyRequests: %d", nonReadyCount) - framework.Logf("TG Health Events: %d", len(allEvents)) - - if nonReadyCount > 0 { - framework.Logf("") - framework.Logf("PRE-READYZ ROUTING DETECTED (OCPBUGS-86789):") - for _, d := range nonReadyDetails { - framework.Logf("%s", d) - } + if tl.PreReadyzReqCount > 0 { + report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to pre-readyz target(s) — OCPBUGS-86789 reproduced\n", tl.PreReadyzReqCount) + } else { + report += "\nVERDICT: No pre-readyz routing detected in this iteration\n" } - framework.Logf("") - framework.Logf("TG Health Timeline:") - for _, e := range allEvents { - framework.Logf(" t=%s target=%s %s→%s reason=%s", - e.Timestamp.Format(time.RFC3339), e.TargetID, e.PrevState, e.State, e.Reason) + framework.Logf("\n%s", report) + }) + }) + + // ── Scenario 5.2 ─────────────────────────────────────────────────── + Context("shutdown propagation measurement (SPLAT-307)", func() { + It("should stop routing within shutdown-delay after "+ + "readyz starts failing", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) } - framework.Logf("") - if nonReadyCount > 0 { - framework.Logf("VERDICT: NLB routed %d request(s) to pre-readyz target(s)", nonReadyCount) - framework.Logf("This reproduces OCPBUGS-86789 — NLB routes before /readyz passes") - } else { - framework.Logf("VERDICT: No pre-readyz routing detected in this iteration") + replicas := int32(3) + startupDelay := 60 * time.Second + clientInterval := 500 * time.Millisecond + observerInterval := 1 * time.Second + steadyStateDuration := 30 * time.Second + shutdownObserveDuration := 3 * time.Minute + recoveryObserveDuration := 3 * time.Minute + + deployName := "healthserver" + svcName := "healthserver-lb" + + lbDNS, observer, svcCfg := setupHealthTransition( + ctx, cs, ns, deployName, svcName, image, + replicas, startupDelay, observerInterval, + ) + + observer.Start(ctx) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), clientInterval) + client.Start(ctx) + defer func() { client.Stop(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", steadyStateDuration)) + time.Sleep(steadyStateDuration) + + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err) + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("signaling target pod readyz→503 (t5)") + t5 := time.Now() + err = sendAdminSignal(ctx, cs, ns.Name, targetPod, false) + framework.ExpectNoError(err) + + By(fmt.Sprintf("observing shutdown propagation for %s", shutdownObserveDuration)) + time.Sleep(shutdownObserveDuration) + + By("signaling target pod readyz→200 (t8)") + t8 := time.Now() + err = sendAdminSignal(ctx, cs, ns.Name, targetPod, true) + framework.ExpectNoError(err) + + By(fmt.Sprintf("observing recovery for %s", recoveryObserveDuration)) + time.Sleep(recoveryObserveDuration) + + allRecords := client.Records() + allEvents := observer.Events() + + tl := computeTimeline52(targetPod, t5, t8, allRecords, allEvents) + tl.TargetPod = targetPod + tl.TargetNode = targetNode + + report := buildReport("5.2 (Shutdown Propagation / SPLAT-307)", + tl, svcCfg, replicas, startupDelay, 0, + allEvents, observer.Snapshots()) + + report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to unhealthy target after readyz→503\n", tl.UnhealthyReqCount) + if !tl.T7.IsZero() && !tl.T5.IsZero() { + report += fmt.Sprintf("T_route_stop = %s (NLB kept routing after readyz→503)\n", + tl.T7.Sub(tl.T5).Truncate(time.Second)) } - framework.Logf("═══════════════════════════════════════════════════════════") + + framework.Logf("\n%s", report) }) }) }) +// ─── Setup helper ─────────────────────────────────────────────────────────── + +func setupHealthTransition( + ctx context.Context, + cs clientset.Interface, + ns *v1.Namespace, + deployName, svcName, image string, + replicas int32, + startupDelay time.Duration, + observerInterval time.Duration, +) (lbDNS string, observer *health.Observer, cfg serviceConfig) { + + By("creating healthserver Deployment") + deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image) + _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create deployment") + + By("creating NLB Service with /readyz health check") + svc := buildHealthTransitionService(ns.Name, svcName, deployName) + _, err = cs.CoreV1().Services(ns.Name).Create(ctx, svc, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create service") + cfg.ServiceAnnotations = svc.Annotations + + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up health transition resources") + _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, svcName, metav1.DeleteOptions{}) + if lbDNS != "" { + waitForLBDeletion(cleanupCtx, lbDNS) + } + }) + + By("waiting for Deployment rollout") + err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + d, err := cs.AppsV1().Deployments(ns.Name).Get(ctx, deployName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + framework.Logf("deployment ready replicas: %d/%d", d.Status.ReadyReplicas, replicas) + return d.Status.ReadyReplicas >= replicas, nil + }) + framework.ExpectNoError(err, "deployment rollout") + + By("waiting for NLB provisioning") + err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + s, err := cs.CoreV1().Services(ns.Name).Get(ctx, svcName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + if len(s.Status.LoadBalancer.Ingress) > 0 { + lbDNS = s.Status.LoadBalancer.Ingress[0].Hostname + return lbDNS != "", nil + } + return false, nil + }) + framework.ExpectNoError(err, "NLB provisioning") + cfg.LBDNS = lbDNS + + By("discovering NLB and target group in AWS") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + foundLB, err := getAWSLoadBalancerFromDNSName(ctx, elbClient, lbDNS) + framework.ExpectNoError(err, "find NLB") + cfg.LBARN = aws.ToString(foundLB.LoadBalancerArn) + + observer = health.NewObserver(elbClient, observerInterval) + err = observer.DiscoverTargetGroup(ctx, cfg.LBARN) + framework.ExpectNoError(err, "discover target group") + cfg.TGARN = observer.TargetGroupARN() + cfg.TGTargetType = observer.TargetType() + + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + cfg.TGAttributes = tgAttrs + } + + // Fetch TG health check config from the TG itself + fetchTGHealthCheckConfig(ctx, elbClient, &cfg) + + By("waiting for all TG targets to become healthy") + err = observer.WaitForAllHealthy(ctx, int(replicas), 10*time.Minute) + framework.ExpectNoError(err, "targets healthy") + + return lbDNS, observer, cfg +} + +func fetchTGHealthCheckConfig(ctx context.Context, elbClient *elbv2.Client, cfg *serviceConfig) { + out, err := elbClient.DescribeTargetGroups(ctx, &elbv2.DescribeTargetGroupsInput{ + TargetGroupArns: []string{cfg.TGARN}, + }) + if err != nil || len(out.TargetGroups) == 0 { + return + } + tg := out.TargetGroups[0] + cfg.TGAttributes = append(cfg.TGAttributes, + health.TGAttribute{Key: "_hc_protocol", Value: string(tg.HealthCheckProtocol)}, + health.TGAttribute{Key: "_hc_port", Value: aws.ToString(tg.HealthCheckPort)}, + health.TGAttribute{Key: "_hc_path", Value: aws.ToString(tg.HealthCheckPath)}, + health.TGAttribute{Key: "_hc_interval_seconds", Value: fmt.Sprintf("%d", aws.ToInt32(tg.HealthCheckIntervalSeconds))}, + health.TGAttribute{Key: "_hc_healthy_threshold", Value: fmt.Sprintf("%d", aws.ToInt32(tg.HealthyThresholdCount))}, + health.TGAttribute{Key: "_hc_unhealthy_threshold", Value: fmt.Sprintf("%d", aws.ToInt32(tg.UnhealthyThresholdCount))}, + ) +} + +// ─── Admin API via K8s API server proxy ───────────────────────────────────── + +func sendAdminSignal(ctx context.Context, cs clientset.Interface, namespace, podName string, ready bool) error { + readyStr := "false" + if ready { + readyStr = "true" + } + result := cs.CoreV1().RESTClient().Post(). + AbsPath(fmt.Sprintf("/api/v1/namespaces/%s/pods/%s:8080/proxy/admin/readyz", namespace, podName)). + Param("ready", readyStr). + Do(ctx) + return result.Error() +} + +// ─── Pod lifecycle helpers ────────────────────────────────────────────────── + +func waitForNewPod(ctx context.Context, cs clientset.Interface, namespace, deployName, oldPodName string) string { + var newPod string + err := wait.PollUntilContextTimeout(ctx, 2*time.Second, 3*time.Minute, true, func(ctx context.Context) (bool, error) { + pods, err := cs.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + if err != nil { + return false, nil + } + for i := range pods.Items { + p := &pods.Items[i] + if p.Name == oldPodName || p.DeletionTimestamp != nil { + continue + } + if p.Status.Phase == v1.PodRunning { + newPod = p.Name + return true, nil + } + } + return false, nil + }) + framework.ExpectNoError(err, "wait for replacement pod") + return newPod +} + +// ─── Timeline computation ─────────────────────────────────────────────────── + +func computeTimeline( + oldPod string, + knownServers map[string]bool, + t5, t71 time.Time, + records []health.RequestRecord, + events []health.HealthEvent, +) transitionTimeline { + tl := transitionTimeline{T5: t5, T71: t71} + + // t6: first observer event showing a target transitioning healthy→unhealthy + // AFTER t5 (when we signaled readyz→503). Excludes initial→unhealthy which + // are nodes that never had local pods and failed HC from the start. + for _, e := range events { + if e.Timestamp.Before(t5) { + continue + } + if e.State == "unhealthy" && e.PrevState == "healthy" { + tl.T6 = e.Timestamp + break + } + } + + // t7: last request served by the OLD target pod after t5. + // Each request after readyz→503 counts as an "unhealthy" request. + for _, r := range records { + if r.Timestamp.Before(t5) { + continue + } + if r.ServerID == oldPod { + tl.T7 = r.Timestamp + tl.UnhealthyReqCount++ + } + } + + // Identify the new pod: first ServerID not in knownServers, after t7.1 + for _, r := range records { + if r.ServerID == "" || knownServers[r.ServerID] || r.Timestamp.Before(t71) { + continue + } + tl.NewPod = r.ServerID + break + } + + // Now process only responses from the identified new pod + for _, r := range records { + if r.ServerID != tl.NewPod || r.Timestamp.Before(t71) { + continue + } + + // t7.3: first response from new pod (approximates TCP up) + if tl.T73.IsZero() { + tl.T73 = r.Timestamp + } + + // t7.4: first pre-readyz request from new pod + if r.IsNonReadyReq { + tl.PreReadyzReqCount++ + if tl.T74.IsZero() { + tl.T74 = r.Timestamp + } + } + + // t8: when the new pod's /readyz first returned 200 (from header, local time) + if tl.T8.IsZero() && r.ServerState == "ready" && r.FirstReadyzTime != "never" && r.FirstReadyzTime != "" { + if parsed, err := time.Parse(time.RFC3339Nano, r.FirstReadyzTime); err == nil { + tl.T8 = parsed.Local() + } + } + + // t10: first client request where new pod reports "ready" + if tl.T10.IsZero() && r.ServerState == "ready" { + tl.T10 = r.Timestamp + } + } + + // t9: first observer healthy event AFTER t71 (pod restart), not after t8 + // (t8 may be wrong or zero). Look for the healthy transition that corresponds + // to the new pod coming online. + for _, e := range events { + if e.Timestamp.Before(t71) { + continue + } + if e.State == "healthy" && (e.PrevState == "unhealthy" || e.PrevState == "initial") { + tl.T9 = e.Timestamp + break + } + } + + return tl +} + +// computeTimeline52 builds the timing model for Scenario 5.2 (no restart). +// The target pod stays alive; we signal readyz→503, observe shutdown propagation, +// then signal readyz→200 and observe recovery. +func computeTimeline52( + targetPod string, + t5, t8 time.Time, + records []health.RequestRecord, + events []health.HealthEvent, +) transitionTimeline { + tl := transitionTimeline{T5: t5, T8: t8} + + // t6: first observer event showing a target going unhealthy AFTER t5. + // Only match healthy→unhealthy transitions (not initial→unhealthy which + // are nodes that never passed HC, e.g. nodes without local pods). + for _, e := range events { + if e.Timestamp.Before(t5) { + continue + } + if e.State == "unhealthy" && e.PrevState == "healthy" { + tl.T6 = e.Timestamp + break + } + } + + // t7: last request served by the target pod after t5 and before t8. + // Each such request is "unhealthy" because readyz was 503. + for _, r := range records { + if r.Timestamp.Before(t5) || r.Timestamp.After(t8) { + continue + } + if r.ServerID == targetPod { + tl.T7 = r.Timestamp + tl.UnhealthyReqCount++ + } + } + + // t9: first observer event showing a target going healthy AFTER t8. + // Match unhealthy→healthy (recovery after we signaled readyz→200). + for _, e := range events { + if e.Timestamp.Before(t8) { + continue + } + if e.State == "healthy" && e.PrevState == "unhealthy" { + tl.T9 = e.Timestamp + break + } + } + + // t10: first request to the target pod after recovery (after t9 if known, + // otherwise after t8). + searchAfter := t8 + if !tl.T9.IsZero() { + searchAfter = tl.T9 + } + for _, r := range records { + if r.Timestamp.Before(searchAfter) { + continue + } + if r.ServerID == targetPod { + tl.T10 = r.Timestamp + break + } + } + + return tl +} + +// ─── Report (single block, no per-line logger timestamps) ─────────────────── + +func fmtT(t time.Time) string { + if t.IsZero() { + return "N/A" + } + return t.Format(time.RFC3339) +} + +func fmtDelta(base, t time.Time) string { + if t.IsZero() || base.IsZero() { + return "" + } + return fmt.Sprintf("[+%s]", t.Sub(base).Truncate(time.Millisecond)) +} + +func fmtDur(a, b time.Time) string { + if a.IsZero() || b.IsZero() { + return "N/A" + } + return b.Sub(a).Truncate(time.Millisecond).String() +} + +// timelineEntry is a single row in the unified chronological timeline. +type timelineEntry struct { + t time.Time + label string + delta string +} + +func buildReport( + scenario string, + tl transitionTimeline, + cfg serviceConfig, + replicas int32, + startupDelay, shutdownDelay time.Duration, + events []health.HealthEvent, + snapshots []health.TargetSnapshot, +) string { + var b strings.Builder + w := func(format string, args ...any) { fmt.Fprintf(&b, format+"\n", args...) } + sep := "═══════════════════════════════════════════════════════════════════════════" + + w(sep) + w("HEALTH TRANSITION REPORT — Scenario %s", scenario) + w(sep) + + // ── Identity ── + w("") + w("TARGET") + w(" Pod: %s", tl.TargetPod) + w(" Node: %s", tl.TargetNode) + if tl.NewPod != "" { + w(" New Pod: %s", tl.NewPod) + } + + // ── Test params ── + w("") + w("TEST PARAMETERS") + w(" Replicas: %d", replicas) + w(" Startup Delay: %s", startupDelay) + if shutdownDelay > 0 { + w(" Shutdown Delay: %s", shutdownDelay) + } + + // ── Service config ── + w("") + w("SERVICE CONFIGURATION") + w(" LB DNS: %s", cfg.LBDNS) + w(" LB ARN: %s", cfg.LBARN) + for k, v := range cfg.ServiceAnnotations { + short := strings.TrimPrefix(k, "service.beta.kubernetes.io/aws-load-balancer-") + w(" svc/%s: %s", short, v) + } + + // ── TG config ── + w("") + w("TARGET GROUP CONFIGURATION") + w(" TG ARN: %s", cfg.TGARN) + w(" Target Type: %s", cfg.TGTargetType) + for _, a := range cfg.TGAttributes { + if a.Key == "" { + continue + } + w(" %s: %s", a.Key, a.Value) + } + + // ── Timing table ── + w("") + w("TIMING TABLE") + w("%-25s %-14s %-14s %s", "Metric", "Value", "Expected", "Description") + w("%-25s %-14s %-14s %s", strings.Repeat("─", 25), strings.Repeat("─", 14), strings.Repeat("─", 14), strings.Repeat("─", 30)) + w("%-25s %-14s %-14s %s", "T_tg_unhealthy", fmtDur(tl.T5, tl.T6), "~20s", "t6-t5: HC detect unhealthy") + w("%-25s %-14s %-14s %s", "T_route_stop", fmtDur(tl.T5, tl.T7), " 0 { + first := snapshots[0] + last := snapshots[len(snapshots)-1] + w("") + w("TG SNAPSHOTS (%d polls, %s duration)", len(snapshots), + last.Timestamp.Sub(first.Timestamp).Truncate(time.Second)) + w(" first: %s healthy=%d unhealthy=%d initial=%d", + fmtT(first.Timestamp), first.HealthyCount, first.UnhealthyCount, first.InitialCount) + w(" last: %s healthy=%d unhealthy=%d initial=%d", + fmtT(last.Timestamp), last.HealthyCount, last.UnhealthyCount, last.InitialCount) + } + + w(sep) + return b.String() +} + +// ─── Resource builders ────────────────────────────────────────────────────── + func buildHealthserverDeployment(namespace, name string, replicas int32, startupDelay time.Duration, image string) *appsv1.Deployment { labels := map[string]string{"app": name} return &appsv1.Deployment{ @@ -310,17 +803,13 @@ func buildHealthTransitionService(namespace, name, deployName string) *v1.Servic func waitForLBDeletion(ctx context.Context, lbDNS string) { elbClient, err := createAWSClientLoadBalancer(ctx) if err != nil { - framework.Logf("failed to create ELB client for cleanup: %v", err) return } - err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + _ = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { lb, err := findAWSLoadBalancerByDNSName(ctx, elbClient, lbDNS) if err != nil { return false, nil } return lb == nil, nil }) - if err != nil { - framework.Logf("warning: timed out waiting for LB deletion: %v", err) - } } From 44f55afee090d3341010f28c95360e833795b9cd Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 01:30:59 -0300 Subject: [PATCH 04/22] e2e: master-node targeting, cross-zone, CAPA variant, t0-t4 timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schedule healthserver pods on control-plane nodes to match KAS topology: - nodeSelector for node-role.kubernetes.io/master - tolerations for master and control-plane taints - target-node-labels annotation filters NLB targets to master nodes only (eliminates initial→unhealthy noise from worker nodes without pods) Enable cross-zone load balancing for HA parity with production NLBs via the aws-load-balancer-cross-zone-load-balancing-enabled annotation. Wait for ALL TG targets to report healthy (zero unhealthy/initial) before starting the test, instead of just waiting for N=replicas. This ensures Hyperplane has fully converged before measurements begin. Add CAPA variant test (Scenario 5.5-CAPA) that applies TG attributes via ModifyTargetGroupAttributes after TG creation: target_health_state.unhealthy.connection_termination.enabled=false target_health_state.unhealthy.draining_interval_seconds=300 Re-fetches TG config after modification so the report reflects the actual TG state during the test. Add initial registration timers (t0-t4) to transitionTimeline: t0: deployment created t1: pods running t2: NLB provisioned t3: all TG targets healthy t4: first client request These appear in both the TIMING TABLE and the chronological TIMELINE. Increase client request rate to 200ms (5 req/s) for better data density. Increase steady state baseline to 2min and post-restart observation to 5min for more reliable measurements. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/observer.go | 21 ++ .../e2e/aws/lb_health_transition.go | 331 ++++++++++++++++-- 2 files changed, 320 insertions(+), 32 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go index 1e541d6a1..5368460fe 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go @@ -84,6 +84,27 @@ func (o *Observer) DescribeTGAttributes(ctx context.Context) ([]TGAttribute, err return attrs, nil } +// ModifyTGAttributes sets target group attributes via the AWS API. +// Used to apply TG configuration variations (e.g., CAPA fix attributes) +// after the TG is created by the cloud controller. +func (o *Observer) ModifyTGAttributes(ctx context.Context, attrs map[string]string) error { + var kv []elbv2types.TargetGroupAttribute + for k, v := range attrs { + kv = append(kv, elbv2types.TargetGroupAttribute{ + Key: aws.String(k), + Value: aws.String(v), + }) + } + _, err := o.elbClient.ModifyTargetGroupAttributes(ctx, &elbv2.ModifyTargetGroupAttributesInput{ + TargetGroupArn: aws.String(o.tgARN), + Attributes: kv, + }) + if err != nil { + return fmt.Errorf("modify TG attributes: %w", err) + } + return nil +} + // WaitForAllHealthy blocks until at least minHealthy targets report // TargetHealthStateEnumHealthy, or the timeout is reached. func (o *Observer) WaitForAllHealthy(ctx context.Context, minHealthy int, timeout time.Duration) error { diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index e9453bef5..1d23c2f64 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -32,6 +32,18 @@ const ( // margin), simulating how long KAS keeps serving after /readyz→503 before // the process exits. CKAO sets 135s; we add buffer for HC propagation. kasShutdownDelay = 192 * time.Second + + // defaultClientInterval controls how often the HTTP client sends requests + // through the NLB. Lower values increase load density for propagation testing. + defaultClientInterval = 200 * time.Millisecond + + // defaultSteadyState is how long we observe healthy traffic before triggering + // the test scenario. Must be long enough for all replicas to receive traffic. + defaultSteadyState = 2 * time.Minute + + // postRestartObserve is how long we observe after the new pod starts. + // Must be long enough for NLB HC + Hyperplane propagation to complete. + postRestartObserve = 5 * time.Minute ) // transitionTimeline captures all timing milestones from the SPLAT-307 state @@ -39,6 +51,13 @@ const ( // // A zero time.Time means the milestone was not observed. type transitionTimeline struct { + // Initial registration phase (t0-t4, captured during setup) + T0 time.Time // deployment created (pods scheduling) + T1 time.Time // deployment ready (all pods Running) + T2 time.Time // NLB provisioned (LB DNS assigned) + T3 time.Time // all TG targets healthy (HC passed + propagated) + T4 time.Time // first client request received (NLB routing established) + // Shutdown phase (SPLAT-307 path) T5 time.Time // readyz→503 signal sent T6 time.Time // first observer event: target unhealthy @@ -99,25 +118,25 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { replicas := int32(3) startupDelay := 60 * time.Second shutdownDelay := kasShutdownDelay - clientInterval := 500 * time.Millisecond - observerInterval := 1 * time.Second - steadyStateDuration := 30 * time.Second deployName := "healthserver" svcName := "healthserver-lb" - lbDNS, observer, svcCfg := setupHealthTransition( + // Setup creates NLB targeting master nodes, waits for ALL targets healthy + lbDNS, observer, svcCfg, setupTimes := setupHealthTransition( ctx, cs, ns, deployName, svcName, image, - replicas, startupDelay, observerInterval, + replicas, startupDelay, ) observer.Start(ctx) - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), clientInterval) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) client.Start(ctx) defer func() { client.Stop(); observer.Stop() }() - By(fmt.Sprintf("verifying steady state for %s", steadyStateDuration)) - time.Sleep(steadyStateDuration) + // Steady state: long enough for all replicas to receive traffic and + // for the NLB to establish stable routing patterns. + By(fmt.Sprintf("verifying steady state for %s", defaultSteadyState)) + time.Sleep(defaultSteadyState) steadyRecords := client.Records() steadyNonReady := 0 @@ -126,6 +145,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { steadyNonReady++ } } + framework.Logf("[steady] %d requests, %d non-ready", len(steadyRecords), steadyNonReady) Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ @@ -144,14 +164,18 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { targetPod := pods.Items[0].Name targetNode := pods.Items[0].Spec.NodeName + // t5: Signal readyz→503 — simulates KAS receiving SIGTERM By("signaling target pod readyz→503 (t5)") t5 := time.Now() err = sendAdminSignal(ctx, cs, ns.Name, targetPod, false) framework.ExpectNoError(err, "signal readyz→false") + // Wait shutdown-delay — simulates KAS shutdown-delay-duration (192s) + // during which the pod keeps serving but /readyz returns 503 By(fmt.Sprintf("waiting %s shutdown-delay before pod deletion", shutdownDelay)) time.Sleep(shutdownDelay) + // t7.1: Delete pod — simulates KAS process exit By("deleting target pod (t7.1)") t71 := time.Now() err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) @@ -160,7 +184,8 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By("waiting for replacement pod") newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) - observeDuration := startupDelay + 3*time.Minute + // Observe long enough for: startup-delay + HC threshold + Hyperplane propagation + observeDuration := startupDelay + postRestartObserve By(fmt.Sprintf("observing for %s (startup-delay + propagation buffer)", observeDuration)) time.Sleep(observeDuration) @@ -168,6 +193,18 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { allEvents := observer.Events() tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + // Copy setup-phase timers (t0-t3) into the timeline + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + // t4: first successful client request (NLB routing established) + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } tl.TargetPod = targetPod tl.TargetNode = targetNode tl.NewPod = newPod @@ -186,6 +223,132 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { }) }) + // ── Scenario 5.5 variant with CAPA TG attributes ──────────────────── + // Same as 5.5 but applies the CAPA fix TG attributes after TG creation: + // target_health_state.unhealthy.connection_termination.enabled = false + // target_health_state.unhealthy.draining_interval_seconds = 300 + // This simulates the NLB configuration applied by CAPA (OCPBUGS-55626). + Context("pre-readyz routing with CAPA TG attributes (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with connection-termination disabled and draining=300s", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + replicas := int32(3) + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + + deployName := "healthserver" + svcName := "healthserver-lb" + + lbDNS, observer, svcCfg, setupTimes := setupHealthTransition( + ctx, cs, ns, deployName, svcName, image, + replicas, startupDelay, + ) + + // Apply CAPA fix TG attributes BEFORE collecting TG config for report + // and BEFORE starting the observer/client. + capaAttrs := map[string]string{ + "target_health_state.unhealthy.connection_termination.enabled": "false", + "target_health_state.unhealthy.draining_interval_seconds": "300", + } + By("applying CAPA TG attributes (conn_term=false, draining=300s)") + err := observer.ModifyTGAttributes(ctx, capaAttrs) + framework.ExpectNoError(err, "modify TG attributes for CAPA variant") + + // Re-fetch TG attributes so the report reflects the modified config + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) + client.Start(ctx) + defer func() { client.Stop(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", defaultSteadyState)) + time.Sleep(defaultSteadyState) + + steadyRecords := client.Records() + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err) + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + for _, p := range pods.Items { + knownServers[p.Name] = true + } + + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("signaling target pod readyz→503 (t5)") + t5 := time.Now() + err = sendAdminSignal(ctx, cs, ns.Name, targetPod, false) + framework.ExpectNoError(err, "signal readyz→false") + + By(fmt.Sprintf("waiting %s shutdown-delay before pod deletion", shutdownDelay)) + time.Sleep(shutdownDelay) + + By("deleting target pod (t7.1)") + t71 := time.Now() + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod") + newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) + + observeDuration := startupDelay + postRestartObserve + By(fmt.Sprintf("observing for %s (startup-delay + propagation buffer)", observeDuration)) + time.Sleep(observeDuration) + + allRecords := client.Records() + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + + report := buildReport("5.5-CAPA (Pre-Readyz + conn_term=false draining=300s)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allEvents, observer.Snapshots()) + + if tl.PreReadyzReqCount > 0 { + report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to pre-readyz target(s) — OCPBUGS-86789 reproduced (CAPA config)\n", tl.PreReadyzReqCount) + } else { + report += "\nVERDICT: No pre-readyz routing detected with CAPA TG attributes\n" + } + + framework.Logf("\n%s", report) + }) + }) + // ── Scenario 5.2 ─────────────────────────────────────────────────── Context("shutdown propagation measurement (SPLAT-307)", func() { It("should stop routing within shutdown-delay after "+ @@ -198,27 +361,24 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { replicas := int32(3) startupDelay := 60 * time.Second - clientInterval := 500 * time.Millisecond - observerInterval := 1 * time.Second - steadyStateDuration := 30 * time.Second shutdownObserveDuration := 3 * time.Minute recoveryObserveDuration := 3 * time.Minute deployName := "healthserver" svcName := "healthserver-lb" - lbDNS, observer, svcCfg := setupHealthTransition( + lbDNS, observer, svcCfg, setupTimes := setupHealthTransition( ctx, cs, ns, deployName, svcName, image, - replicas, startupDelay, observerInterval, + replicas, startupDelay, ) observer.Start(ctx) - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), clientInterval) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) client.Start(ctx) defer func() { client.Stop(); observer.Stop() }() - By(fmt.Sprintf("verifying steady state for %s", steadyStateDuration)) - time.Sleep(steadyStateDuration) + By(fmt.Sprintf("verifying steady state for %s", defaultSteadyState)) + time.Sleep(defaultSteadyState) pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployName), @@ -247,6 +407,17 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { allEvents := observer.Events() tl := computeTimeline52(targetPod, t5, t8, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + // t4: first successful client request + for _, r := range client.Records() { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } tl.TargetPod = targetPod tl.TargetNode = targetNode @@ -267,6 +438,11 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { // ─── Setup helper ─────────────────────────────────────────────────────────── +// setupHealthTransition creates the healthserver Deployment and NLB Service, +// discovers the TG, fetches TG config, and waits for ALL TG targets to be +// healthy before returning. Pods are scheduled on master/control-plane nodes +// to match KAS topology. The NLB targets only master nodes via the +// target-node-labels annotation. Cross-zone load balancing is enabled. func setupHealthTransition( ctx context.Context, cs clientset.Interface, @@ -274,15 +450,16 @@ func setupHealthTransition( deployName, svcName, image string, replicas int32, startupDelay time.Duration, - observerInterval time.Duration, -) (lbDNS string, observer *health.Observer, cfg serviceConfig) { +) (lbDNS string, observer *health.Observer, cfg serviceConfig, setupTimes transitionTimeline) { - By("creating healthserver Deployment") + // t0: deployment created — pods begin scheduling on master nodes + By("creating healthserver Deployment (scheduled on master nodes)") deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image) + setupTimes.T0 = time.Now() _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) framework.ExpectNoError(err, "create deployment") - By("creating NLB Service with /readyz health check") + By("creating NLB Service (master-only targets, cross-zone, /readyz HC)") svc := buildHealthTransitionService(ns.Name, svcName, deployName) _, err = cs.CoreV1().Services(ns.Name).Create(ctx, svc, metav1.CreateOptions{}) framework.ExpectNoError(err, "create service") @@ -307,6 +484,8 @@ func setupHealthTransition( return d.Status.ReadyReplicas >= replicas, nil }) framework.ExpectNoError(err, "deployment rollout") + // t1: all pods running (startup-delay may still be in progress) + setupTimes.T1 = time.Now() By("waiting for NLB provisioning") err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { @@ -321,6 +500,8 @@ func setupHealthTransition( return false, nil }) framework.ExpectNoError(err, "NLB provisioning") + // t2: NLB provisioned, DNS assigned + setupTimes.T2 = time.Now() cfg.LBDNS = lbDNS By("discovering NLB and target group in AWS") @@ -331,28 +512,71 @@ func setupHealthTransition( framework.ExpectNoError(err, "find NLB") cfg.LBARN = aws.ToString(foundLB.LoadBalancerArn) - observer = health.NewObserver(elbClient, observerInterval) + observer = health.NewObserver(elbClient, 1*time.Second) err = observer.DiscoverTargetGroup(ctx, cfg.LBARN) framework.ExpectNoError(err, "discover target group") cfg.TGARN = observer.TargetGroupARN() cfg.TGTargetType = observer.TargetType() + // Fetch TG attributes and HC config for the report tgAttrs, err := observer.DescribeTGAttributes(ctx) if err == nil { cfg.TGAttributes = tgAttrs } + fetchTGHealthCheckConfig(ctx, &cfg) + + // Wait for ALL registered TG targets to be healthy (not just N replicas). + // With master-only node targeting, this should be exactly 3 targets. + // Previously we waited for minHealthy=replicas which could pass with + // worker-node targets while master-node targets were still "initial". + By("waiting for ALL TG targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all TG targets healthy") + // t3: all TG targets healthy — HC passed and propagated through Hyperplane + setupTimes.T3 = time.Now() + + return lbDNS, observer, cfg, setupTimes +} - // Fetch TG health check config from the TG itself - fetchTGHealthCheckConfig(ctx, elbClient, &cfg) - - By("waiting for all TG targets to become healthy") - err = observer.WaitForAllHealthy(ctx, int(replicas), 10*time.Minute) - framework.ExpectNoError(err, "targets healthy") - - return lbDNS, observer, cfg +// waitForAllTGTargetsHealthy blocks until every registered target reports +// healthy (zero unhealthy, zero initial). This ensures the NLB data plane +// has fully converged before the test starts. +func waitForAllTGTargetsHealthy(ctx context.Context, observer *health.Observer, timeout time.Duration) error { + return wait.PollUntilContextTimeout(ctx, 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) { + snaps := observer.Snapshots() + // Do a live poll by starting/stopping temporarily, or just call the + // observer's underlying API. For simplicity, trigger one poll by + // checking WaitForAllHealthy with a high count. + // Instead, use the observer's ELB client directly via DescribeTGAttributes trick: + // Actually, let's just use WaitForAllHealthy with count=0 sentinel and + // check via snapshots. Simpler: poll the API directly here. + events := observer.Events() + if len(events) == 0 { + // Observer hasn't polled yet; trigger a manual check + return false, nil + } + // Check the latest snapshot if available + if len(snaps) > 0 { + last := snaps[len(snaps)-1] + total := last.HealthyCount + last.UnhealthyCount + last.InitialCount + last.DrainingCount + if total > 0 && last.UnhealthyCount == 0 && last.InitialCount == 0 && last.DrainingCount == 0 { + framework.Logf("all %d TG targets healthy", last.HealthyCount) + return true, nil + } + framework.Logf("TG targets: healthy=%d unhealthy=%d initial=%d draining=%d", + last.HealthyCount, last.UnhealthyCount, last.InitialCount, last.DrainingCount) + } + return false, nil + }) } -func fetchTGHealthCheckConfig(ctx context.Context, elbClient *elbv2.Client, cfg *serviceConfig) { +// fetchTGHealthCheckConfig reads the TG's health check settings from the AWS API +// and appends them to the serviceConfig for report output. +func fetchTGHealthCheckConfig(ctx context.Context, cfg *serviceConfig) { + elbClient, err := createAWSClientLoadBalancer(ctx) + if err != nil { + return + } out, err := elbClient.DescribeTargetGroups(ctx, &elbv2.DescribeTargetGroupsInput{ TargetGroupArns: []string{cfg.TGARN}, }) @@ -372,6 +596,9 @@ func fetchTGHealthCheckConfig(ctx context.Context, elbClient *elbv2.Client, cfg // ─── Admin API via K8s API server proxy ───────────────────────────────────── +// sendAdminSignal sends a readyz control signal to a healthserver pod via the +// K8s API server pod proxy endpoint. This avoids the need for port-forward +// or exec (the healthserver container is FROM scratch, no shell). func sendAdminSignal(ctx context.Context, cs clientset.Interface, namespace, podName string, ready bool) error { readyStr := "false" if ready { @@ -413,6 +640,9 @@ func waitForNewPod(ctx context.Context, cs clientset.Interface, namespace, deplo // ─── Timeline computation ─────────────────────────────────────────────────── +// computeTimeline builds the full timing model for Scenario 5.5 from raw +// client records and observer events. See transitionTimeline for the t-value +// definitions aligned with the SPLAT-307 state machine. func computeTimeline( oldPod string, knownServers map[string]bool, @@ -475,7 +705,9 @@ func computeTimeline( } } - // t8: when the new pod's /readyz first returned 200 (from header, local time) + // t8: when the new pod's /readyz first returned 200 (from header, local time). + // The healthserver runs in UTC inside the container; we convert to local + // time to match the test's clock for consistent delta calculations. if tl.T8.IsZero() && r.ServerState == "ready" && r.FirstReadyzTime != "never" && r.FirstReadyzTime != "" { if parsed, err := time.Parse(time.RFC3339Nano, r.FirstReadyzTime); err == nil { tl.T8 = parsed.Local() @@ -635,6 +867,7 @@ func buildReport( if shutdownDelay > 0 { w(" Shutdown Delay: %s", shutdownDelay) } + w(" Client Interval: %s", defaultClientInterval) // ── Service config ── w("") @@ -663,6 +896,11 @@ func buildReport( w("TIMING TABLE") w("%-25s %-14s %-14s %s", "Metric", "Value", "Expected", "Description") w("%-25s %-14s %-14s %s", strings.Repeat("─", 25), strings.Repeat("─", 14), strings.Repeat("─", 14), strings.Repeat("─", 30)) + w("%-25s %-14s %-14s %s", "T_deploy_ready", fmtDur(tl.T0, tl.T1), "", "t1-t0: pods scheduled + running") + w("%-25s %-14s %-14s %s", "T_nlb_provision", fmtDur(tl.T0, tl.T2), "", "t2-t0: NLB provisioned") + w("%-25s %-14s %-14s %s", "T_tg_initial_healthy", fmtDur(tl.T0, tl.T3), "", "t3-t0: all TG targets healthy") + w("%-25s %-14s %-14s %s", "T_first_request", fmtDur(tl.T3, tl.T4), "seconds", "t4-t3: first routed request") + w("%-25s %-14s %-14s %s", "", "", "", "") w("%-25s %-14s %-14s %s", "T_tg_unhealthy", fmtDur(tl.T5, tl.T6), "~20s", "t6-t5: HC detect unhealthy") w("%-25s %-14s %-14s %s", "T_route_stop", fmtDur(tl.T5, tl.T7), " Date: Thu, 6 Aug 2026 01:40:41 -0300 Subject: [PATCH 05/22] e2e: 90s post-healthy observation, environment summary in report Replace fixed-duration observation windows with event-driven waits: - After initial setup: wait for ALL TG targets healthy (already done), then observe for 90s to confirm stable routing. - After pod restart: wait for restarted target to become healthy via waitForAllTGTargetsHealthy(), then observe 90s post-recovery. Previously used a fixed startup-delay+5min which was either too short (missed late propagation) or too long (wasted time). Add ENVIRONMENT section to the report with platform, region, and topology (HighlyAvailable vs External/HyperShift) from the cluster's Infrastructure resource. Gives full visibility of the test setup alongside the SERVICE CONFIGURATION and TARGET GROUP CONFIGURATION. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../e2e/aws/lb_health_transition.go | 76 +++++++++++++------ 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 1d23c2f64..18fae6151 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -13,6 +13,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/aws/health" + "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/common" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,13 +38,10 @@ const ( // through the NLB. Lower values increase load density for propagation testing. defaultClientInterval = 200 * time.Millisecond - // defaultSteadyState is how long we observe healthy traffic before triggering - // the test scenario. Must be long enough for all replicas to receive traffic. - defaultSteadyState = 2 * time.Minute - - // postRestartObserve is how long we observe after the new pod starts. - // Must be long enough for NLB HC + Hyperplane propagation to complete. - postRestartObserve = 5 * time.Minute + // postHealthyObserve is how long we continue observing after all targets + // become healthy (both initial setup and post-restart). 90s gives enough + // time to confirm stable routing while keeping test duration reasonable. + postHealthyObserve = 90 * time.Second ) // transitionTimeline captures all timing milestones from the SPLAT-307 state @@ -83,7 +81,7 @@ type transitionTimeline struct { NewPod string } -// serviceConfig records Service and TG configuration for the report. +// serviceConfig records Service, TG, and environment configuration for the report. type serviceConfig struct { ServiceAnnotations map[string]string TGAttributes []health.TGAttribute @@ -91,6 +89,11 @@ type serviceConfig struct { TGTargetType string LBARN string LBDNS string + + // Environment summary + Region string + Platform string // e.g., "AWS" + Topology string // e.g., "HighlyAvailable" } var _ = Describe(healthTransitionTestPrefix+" NLB", func() { @@ -133,10 +136,10 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { client.Start(ctx) defer func() { client.Stop(); observer.Stop() }() - // Steady state: long enough for all replicas to receive traffic and - // for the NLB to establish stable routing patterns. - By(fmt.Sprintf("verifying steady state for %s", defaultSteadyState)) - time.Sleep(defaultSteadyState) + // Steady state: 90s after all targets healthy — confirms stable + // routing to all replicas before triggering the test scenario. + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) steadyRecords := client.Records() steadyNonReady := 0 @@ -184,10 +187,14 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By("waiting for replacement pod") newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) - // Observe long enough for: startup-delay + HC threshold + Hyperplane propagation - observeDuration := startupDelay + postRestartObserve - By(fmt.Sprintf("observing for %s (startup-delay + propagation buffer)", observeDuration)) - time.Sleep(observeDuration) + // Wait for the restarted target to become healthy again, then observe + // for postHealthyObserve to confirm stable routing. + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) allRecords := client.Records() allEvents := observer.Events() @@ -271,8 +278,8 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { client.Start(ctx) defer func() { client.Stop(); observer.Stop() }() - By(fmt.Sprintf("verifying steady state for %s", defaultSteadyState)) - time.Sleep(defaultSteadyState) + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) steadyRecords := client.Records() steadyNonReady := 0 @@ -313,9 +320,12 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By("waiting for replacement pod") newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) - observeDuration := startupDelay + postRestartObserve - By(fmt.Sprintf("observing for %s (startup-delay + propagation buffer)", observeDuration)) - time.Sleep(observeDuration) + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) allRecords := client.Records() allEvents := observer.Events() @@ -377,8 +387,8 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { client.Start(ctx) defer func() { client.Stop(); observer.Stop() }() - By(fmt.Sprintf("verifying steady state for %s", defaultSteadyState)) - time.Sleep(defaultSteadyState) + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployName), @@ -465,6 +475,19 @@ func setupHealthTransition( framework.ExpectNoError(err, "create service") cfg.ServiceAnnotations = svc.Annotations + // Populate environment summary from the cluster's Infrastructure resource + cfg.Platform = "AWS" + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + cfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + cfg.Topology = "External (HyperShift)" + } else { + cfg.Topology = "HighlyAvailable" + } + } + DeferCleanup(func(cleanupCtx context.Context) { framework.Logf("cleaning up health transition resources") _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) @@ -850,6 +873,13 @@ func buildReport( w("HEALTH TRANSITION REPORT — Scenario %s", scenario) w(sep) + // ── Environment ── + w("") + w("ENVIRONMENT") + w(" Platform: %s", cfg.Platform) + w(" Region: %s", cfg.Region) + w(" Topology: %s", cfg.Topology) + // ── Identity ── w("") w("TARGET") From c3068f1602f8cdc179571776484793bc2f828f6f Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 02:10:21 -0300 Subject: [PATCH 06/22] e2e: fix TG healthy wait stuck, add PollOnce, use control-plane label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix waitForAllTGTargetsHealthy getting stuck indefinitely: The function read from observer.Snapshots() which requires the observer's background polling loop to be running. During setup the observer is not started yet, so snapshots were always empty and the function spun until the 10min context deadline. Fix by adding Observer.PollOnce() — a single DescribeTargetHealth call that works independently of the background loop. The wait function now calls PollOnce directly with per-target state logging every 10s so the operator can see convergence progress. Switch nodeSelector from deprecated node-role.kubernetes.io/master to node-role.kubernetes.io/control-plane (OCP 5.x). Keep tolerations for both labels for backward compatibility. Update target-node-labels annotation accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/observer.go | 33 +++++++++++ .../e2e/aws/lb_health_transition.go | 59 ++++++++++--------- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go index 5368460fe..142404c80 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/observer.go @@ -60,6 +60,39 @@ func (o *Observer) TargetGroupARN() string { return o.tgARN } // TargetType returns the target type (instance, ip, lambda, alb). func (o *Observer) TargetType() string { return o.targetType } +// PollOnce performs a single DescribeTargetHealth call and returns a snapshot. +// This can be called independently of Start/Stop — useful during setup when +// the background polling loop is not yet running. +func (o *Observer) PollOnce(ctx context.Context) (TargetSnapshot, error) { + output, err := o.elbClient.DescribeTargetHealth(ctx, &elbv2.DescribeTargetHealthInput{ + TargetGroupArn: aws.String(o.tgARN), + }) + if err != nil { + return TargetSnapshot{}, fmt.Errorf("describe target health: %w", err) + } + + snap := TargetSnapshot{ + Timestamp: time.Now(), + Targets: make(map[string]string, len(output.TargetHealthDescriptions)), + } + for _, d := range output.TargetHealthDescriptions { + id := aws.ToString(d.Target.Id) + state := string(d.TargetHealth.State) + snap.Targets[id] = state + switch d.TargetHealth.State { + case elbv2types.TargetHealthStateEnumHealthy: + snap.HealthyCount++ + case elbv2types.TargetHealthStateEnumUnhealthy, elbv2types.TargetHealthStateEnumUnhealthyDraining: + snap.UnhealthyCount++ + case elbv2types.TargetHealthStateEnumInitial: + snap.InitialCount++ + case elbv2types.TargetHealthStateEnumDraining: + snap.DrainingCount++ + } + } + return snap, nil +} + // TGAttribute is a key-value pair from DescribeTargetGroupAttributes. type TGAttribute struct { Key string diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 18fae6151..a93824505 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -561,35 +561,39 @@ func setupHealthTransition( return lbDNS, observer, cfg, setupTimes } -// waitForAllTGTargetsHealthy blocks until every registered target reports -// healthy (zero unhealthy, zero initial). This ensures the NLB data plane -// has fully converged before the test starts. +// waitForAllTGTargetsHealthy polls DescribeTargetHealth directly (via +// observer.PollOnce) until every registered target reports healthy. +// Logs per-target state every 10s so the operator can see convergence. +// This works both during setup (observer not started) and during the test +// (observer running — PollOnce is independent of the background loop). func waitForAllTGTargetsHealthy(ctx context.Context, observer *health.Observer, timeout time.Duration) error { - return wait.PollUntilContextTimeout(ctx, 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) { - snaps := observer.Snapshots() - // Do a live poll by starting/stopping temporarily, or just call the - // observer's underlying API. For simplicity, trigger one poll by - // checking WaitForAllHealthy with a high count. - // Instead, use the observer's ELB client directly via DescribeTGAttributes trick: - // Actually, let's just use WaitForAllHealthy with count=0 sentinel and - // check via snapshots. Simpler: poll the API directly here. - events := observer.Events() - if len(events) == 0 { - // Observer hasn't polled yet; trigger a manual check + lastLog := time.Time{} + return wait.PollUntilContextTimeout(ctx, 5*time.Second, timeout, true, func(ctx context.Context) (bool, error) { + snap, err := observer.PollOnce(ctx) + if err != nil { + framework.Logf("[tg-wait] poll error: %v", err) return false, nil } - // Check the latest snapshot if available - if len(snaps) > 0 { - last := snaps[len(snaps)-1] - total := last.HealthyCount + last.UnhealthyCount + last.InitialCount + last.DrainingCount - if total > 0 && last.UnhealthyCount == 0 && last.InitialCount == 0 && last.DrainingCount == 0 { - framework.Logf("all %d TG targets healthy", last.HealthyCount) - return true, nil + + total := snap.HealthyCount + snap.UnhealthyCount + snap.InitialCount + snap.DrainingCount + allHealthy := total > 0 && snap.UnhealthyCount == 0 && snap.InitialCount == 0 && snap.DrainingCount == 0 + + // Log every 10s or on state change, showing per-target detail + if time.Since(lastLog) >= 10*time.Second || allHealthy { + var details []string + for id, state := range snap.Targets { + details = append(details, fmt.Sprintf("%s=%s", id, state)) } - framework.Logf("TG targets: healthy=%d unhealthy=%d initial=%d draining=%d", - last.HealthyCount, last.UnhealthyCount, last.InitialCount, last.DrainingCount) + framework.Logf("[tg-wait] healthy=%d unhealthy=%d initial=%d total=%d | %s", + snap.HealthyCount, snap.UnhealthyCount, snap.InitialCount, total, + strings.Join(details, ", ")) + lastLog = time.Now() } - return false, nil + + if allHealthy { + framework.Logf("[tg-wait] all %d targets healthy", snap.HealthyCount) + } + return allHealthy, nil }) } @@ -1025,9 +1029,10 @@ func buildHealthserverDeployment(namespace, name string, replicas int32, startup Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{Labels: labels}, Spec: v1.PodSpec{ - // Schedule on master/control-plane nodes to match KAS topology + // Schedule on control-plane nodes to match KAS topology. + // OCP 5.x uses control-plane; OCP 4.x has both labels. NodeSelector: map[string]string{ - "node-role.kubernetes.io/master": "", + "node-role.kubernetes.io/control-plane": "", }, // Tolerate master and control-plane taints Tolerations: []v1.Toleration{ @@ -1073,7 +1078,7 @@ func buildHealthTransitionService(namespace, name, deployName string) *v1.Servic Namespace: namespace, Annotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - "service.beta.kubernetes.io/aws-load-balancer-target-node-labels": "node-role.kubernetes.io/master=", + "service.beta.kubernetes.io/aws-load-balancer-target-node-labels": "node-role.kubernetes.io/control-plane=", "service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true", "service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol": "HTTP", "service.beta.kubernetes.io/aws-load-balancer-healthcheck-path": "/readyz", From de7f1b12e2f5c1ff792c84a837b49999dcfd54b8 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 02:13:11 -0300 Subject: [PATCH 07/22] e2e: log client and observer start times Add explicit log lines when the client and observer start, so the operator can confirm that request generation begins after all TG targets are healthy and see the exact timestamp in the test output. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/lb_health_transition.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index a93824505..199ff6bf3 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -132,8 +132,10 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { ) observer.Start(ctx) + framework.Logf("[observer] started TG health polling (1s interval)") client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) client.Start(ctx) + framework.Logf("[client] started sending requests to %s every %s", lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() // Steady state: 90s after all targets healthy — confirms stable @@ -274,8 +276,10 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { fetchTGHealthCheckConfig(ctx, &svcCfg) observer.Start(ctx) + framework.Logf("[observer] started TG health polling (1s interval)") client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) client.Start(ctx) + framework.Logf("[client] started sending requests to %s every %s", lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) @@ -383,8 +387,10 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { ) observer.Start(ctx) + framework.Logf("[observer] started TG health polling (1s interval)") client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) client.Start(ctx) + framework.Logf("[client] started sending requests to %s every %s", lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) From 013f9c28227a507c3f7be04a27b3d32ffaeb6daf Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 02:31:45 -0300 Subject: [PATCH 08/22] health: add README documenting the NLB health transition test framework Comprehensive reference for humans and agents covering: - Problem statement (OCPBUGS-86789, SPLAT-307) - Architecture and file layout - Complete timing model (t0-t10) with SPLAT-307 correspondence - All three test scenarios (5.5, 5.5-CAPA, 5.2) - Component documentation (healthserver, observer, client) - Infrastructure config (control-plane scheduling, NLB annotations) - How to build, run, and interpret the report output - Related issues and next steps Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/README.md | 322 ++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/health/README.md diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md new file mode 100644 index 000000000..09aa5fabc --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md @@ -0,0 +1,322 @@ +# NLB Health Transition E2E Test Framework + +Measures NLB target health state transition timing during pod lifecycle +events (shutdown, restart) to reproduce and characterize AWS NLB routing +behavior documented in [OCPBUGS-86789](https://redhat.atlassian.net/browse/OCPBUGS-86789) +and [SPLAT-307](https://redhat.atlassian.net/browse/SPLAT-307). + +## Problem + +During HA KAS rollouts on AWS, the NLB routes **new** TCP connections to a +freshly restarted KAS target whose TCP port is open but `/readyz` has not +yet returned HTTP 200 — even though other healthy KAS targets exist. This +is **not** fail-open behavior (SNO is excluded). + +The reverse direction (NLB keeps routing to an unhealthy target after +`/readyz` returns 503) was characterized in SPLAT-307 (2021-2022) and +mitigated with `shutdown-delay-duration=135s` in CKAO. + +## Architecture + +```text +openshift-tests/ccm-aws-tests/ +├── cmd/healthserver/ # Standalone health-controllable HTTP server +│ ├── main.go # /readyz control, X-Server-State headers, admin API +│ └── Dockerfile # Multi-stage scratch build (~10MB) +├── e2e/aws/ +│ ├── lb_health_transition.go # Ginkgo test scenarios (5.5, 5.5-CAPA, 5.2) +│ └── health/ # Extractable package (zero parent-path imports) +│ ├── types.go # HealthEvent, RequestRecord, TargetSnapshot +│ ├── observer.go # TG health polling, PollOnce, TG attribute R/W +│ ├── client.go # HTTP client with httptrace (new TCP per request) +│ └── README.md # This file +``` + +### Design constraints + +- `health/` imports only stdlib, `k8s.io/*`, AWS SDK — no parent paths. + This is the extractable unit if the framework moves to a standalone repo. +- `cmd/healthserver/` is a standalone binary — no K8s, no OCP dependencies. + Built FROM scratch, ~10MB. Used as the test workload inside the cluster. +- Tests reuse existing CCCMO OTE infrastructure: `loadAWSConfig`, + `createAWSClientLoadBalancer`, `getAWSLoadBalancerFromDNSName`, etc. + +## Timing Model (t0–t10) + +Every test reports the same set of timers regardless of scenario. Based on +the SPLAT-307 state machine extended with restart-phase timers for +OCPBUGS-86789. + +```text +INITIAL REGISTRATION: + t0 Deployment created (pods scheduling on control-plane nodes) + t1 All pods Running + t2 NLB provisioned (DNS assigned) + t3 All TG targets healthy (HC passed + Hyperplane propagated) + t4 First client request received + +SHUTDOWN PHASE (SPLAT-307): + t5 /readyz → 503 (admin signal via K8s API server pod proxy) + t6 AWS API reports target unhealthy (DescribeTargetHealth) + t7 Last client request routed to target + +RESTART PHASE (Scenario 5.5 only): + t7.1 Pod delete sent + t7.3 New pod TCP up (first NLB-routed response) + t7.4 First pre-readyz request from new pod (BUG if present) + +STARTUP PHASE: + t8 /readyz → 200 (from X-First-Readyz-Time header or admin signal) + t9 AWS API reports target healthy + t10 First client request to target +``` + +### Computed metrics + +| Metric | Formula | Expected | Description | +|------------------|-------------|-----------------|--------------------------------------| +| T_deploy_ready | t1 - t0 | | Pod scheduling + startup | +| T_nlb_provision | t2 - t0 | | NLB creation in AWS | +| T_tg_initial | t3 - t0 | | Full initial registration | +| T_first_request | t4 - t3 | seconds | First routed request after healthy | +| T_tg_unhealthy | t6 - t5 | ~20s | HC detect unhealthy (2×10s) | +| T_route_stop | t7 - t5 | < shutdown-delay| Hyperplane propagation (shutdown) | +| T_pod_restart | t7.3 - t7.1 | seconds | Pod kill → new pod TCP up | +| T_tg_healthy | t9 - t8 | ~20s | HC detect healthy (2×10s) | +| T_route_start | t10 - t8 | 20-120s | Hyperplane propagation (startup) | +| T_total_cycle | t10 - t5 | | Full shutdown → healthy cycle | + +### SPLAT-307 correspondence + +| SPLAT-307 Metric (2021) | New Timer | +|------------------------------------------------|-----------------| +| Row 0: Total time to NLB transition Unhealthy | T_tg_unhealthy | +| Row 1: Total time receiving requests unhealthy | T_route_stop | +| Row 2: Total requests received unhealthy | Unhealthy_reqs | +| Row 4: Total time to transition to Healthy | T_tg_healthy | +| Row 5: Total time to receive requests healthy | T_route_start | + +## Test Scenarios + +### Scenario 5.5 — Pre-Readyz Routing (OCPBUGS-86789) + +Reproduces the NLB routing to a target before `/readyz` returns 200 while +other healthy targets exist. + +```text +Test name (OTE): + [cloud-provider-aws-e2e-openshift] loadbalancer health-transition NLB + pre-readyz routing detection (OCPBUGS-86789) + should not route to pre-readyz targets when healthy targets are available + +Flow: + 1. Deploy 3 healthserver pods on control-plane nodes (--startup-delay=60s) + 2. Create NLB: control-plane-only targets, cross-zone, HTTP /readyz HC + 3. Wait for ALL TG targets healthy + 4. Start observer + client (200ms interval, new TCP per request) + 5. Observe 90s steady state (all replicas receiving traffic) + 6. Signal target pod readyz→503 via K8s API proxy (t5) + 7. Wait 192s shutdown-delay (simulates KAS shutdown-delay-duration) + 8. Delete pod (t7.1), wait for replacement + 9. Wait for restarted target healthy + 90s post-recovery observation + 10. Report full t0–t10 timing table + unified chronological timeline + +Detection: any response with X-Server-State: pre-readyz = BUG reproduced +``` + +### Scenario 5.5-CAPA — Pre-Readyz with CAPA TG Attributes + +Same as 5.5 but applies TG attributes via `ModifyTargetGroupAttributes` +after TG creation: + +```text +target_health_state.unhealthy.connection_termination.enabled = false +target_health_state.unhealthy.draining_interval_seconds = 300 +``` + +These are the CAPA fix attributes (OCPBUGS-55626). Tests whether they +affect the unhealthy→healthy routing transition. + +### Scenario 5.2 — Shutdown Propagation (SPLAT-307) + +Measures how long the NLB routes to a target after `/readyz` fails (no pod +restart). Revalidates the SPLAT-307 measurements with current AWS +infrastructure. + +```text +Test name (OTE): + [cloud-provider-aws-e2e-openshift] loadbalancer health-transition NLB + shutdown propagation measurement (SPLAT-307) + should stop routing within shutdown-delay after readyz starts failing + +Flow: + 1. Setup (same as 5.5) + 2. Signal readyz→503 (t5), observe 3min shutdown propagation + 3. Signal readyz→200 (t8), observe 3min recovery + 4. Report timing table +``` + +## Components + +### Healthserver (`cmd/healthserver/`) + +Standalone Go HTTP server deployed as the test workload. + +| Endpoint | Purpose | +|-------------------------|-------------------------------------------------| +| `GET /` | Main endpoint. Returns `X-Server-State` header | +| `GET /readyz` | Health check. 200 (ready) or 503 (not ready) | +| `POST /admin/readyz` | Control readyz: `?ready=true` or `?ready=false` | +| `POST /admin/shutdown` | Graceful shutdown with optional `?delay=Ns` | +| `GET /admin/lifecycle` | JSON lifecycle timestamps | + +Response headers on every `GET /`: + +```text +X-Server-State: pre-readyz | ready | draining | shutdown +X-Server-ID: +X-Server-Start-Time: +X-First-Readyz-Time: +``` + +Flags: `--port` (default 8080), `--startup-delay` (default 30s). + +### Observer (`health/observer.go`) + +Polls `DescribeTargetHealth` at configurable interval. Records: + +- **Transition events** (`HealthEvent`): state changes per target +- **Per-poll snapshots** (`TargetSnapshot`): full TG state with counts + +Also provides: + +- `PollOnce(ctx)` — single live API call (works without background loop) +- `DescribeTGAttributes(ctx)` — read TG config for report +- `ModifyTGAttributes(ctx, attrs)` — set TG attributes (CAPA variant) +- `WaitForAllHealthy(ctx, min, timeout)` — block until min targets healthy + +### Client (`health/client.go`) + +HTTP client with `httptrace` hooks. Creates a **new TCP connection** per +request (`DisableKeepAlives`) to match NLB per-connection routing. + +Captures per request: target IP, TCP dial duration, HTTP status, +`X-Server-State`, `X-Server-ID`, `X-First-Readyz-Time`. + +Detection: `IsNonReadyReq = true` when `X-Server-State == "pre-readyz"`. + +## Infrastructure + +### Pod scheduling + +Pods schedule on control-plane nodes to match KAS topology: + +```yaml +nodeSelector: + node-role.kubernetes.io/control-plane: "" +tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule + - key: node-role.kubernetes.io/control-plane + effect: NoSchedule +``` + +### NLB Service annotations + +```yaml +aws-load-balancer-type: nlb +aws-load-balancer-target-node-labels: node-role.kubernetes.io/control-plane= +aws-load-balancer-cross-zone-load-balancing-enabled: "true" +aws-load-balancer-healthcheck-protocol: HTTP +aws-load-balancer-healthcheck-path: /readyz +aws-load-balancer-healthcheck-port: traffic-port +aws-load-balancer-healthcheck-interval: "10" +aws-load-balancer-healthcheck-healthy-threshold: "2" +aws-load-balancer-healthcheck-unhealthy-threshold: "2" +``` + +`externalTrafficPolicy: Local` ensures per-node health tracking. + +### Graceful shutdown simulation + +The test signals the target pod via the K8s API server pod proxy (the +healthserver container is FROM scratch — no shell for exec): + +```text +POST /api/v1/namespaces/{ns}/pods/{pod}:8080/proxy/admin/readyz?ready=false +``` + +Then waits 192s (KAS shutdown-delay-duration) before deleting the pod. + +## How to Run + +```sh +# 1. Build healthserver image +cd openshift-tests/ccm-aws-tests +podman build -t quay.io//healthserver:latest ./cmd/healthserver/ +podman push quay.io//healthserver:latest + +# 2. Build OTE binary +cd ../.. +make cloud-controller-manager-aws-tests-ext + +# 3. Run +export KUBECONFIG=/path/to/kubeconfig +export AWS_REGION=us-east-1 +export HEALTHSERVER_IMAGE=quay.io//healthserver:latest +BIN=./openshift-tests/bin/cloud-controller-manager-aws-tests-ext + +# Run all health-transition tests +while IFS= read -r t; do + echo "=== Running: $t" + $BIN run-test "$t" < /dev/null +done < <($BIN list tests 2>/dev/null \ + | grep -v -E '(I0|INFO)' \ + | jq -r '.[].name' \ + | grep "health-transition") + +# Run a specific scenario +$BIN run-test "...(OCPBUGS-86789) should not route to pre-readyz targets..." +$BIN run-test "...(SPLAT-307) should stop routing within shutdown-delay..." +$BIN run-test "...(OCPBUGS-86789) should not route to pre-readyz targets with connection-termination..." +``` + +## Report Output + +Each test produces a single-block report (one `framework.Logf` call to +avoid per-line logger timestamps) containing: + +- **ENVIRONMENT**: platform, region, topology +- **TARGET**: pod name, node, new pod (if restart) +- **TEST PARAMETERS**: replicas, startup/shutdown delay, client interval +- **SERVICE CONFIGURATION**: LB DNS/ARN, all Service annotations +- **TARGET GROUP CONFIGURATION**: TG ARN, target type, all TG attributes + including health check config +- **TIMING TABLE**: all computed metrics with expected values +- **TIMELINE**: chronological merge of test milestones (t0–t10) and TG + health events, full RFC3339 timestamps with deltas +- **TG SNAPSHOTS**: first/last poll with healthy/unhealthy/initial counts +- **VERDICT**: detection result + +## Related Issues + +| Issue | Status | Relevance | +|---------------|---------------|----------------------------------------------| +| OCPBUGS-86789 | Critical, POST| NLB routing to unhealthy target (primary) | +| OCPBUGS-55626 | Closed/Done | CAPA TG attribute fix (conn termination) | +| OCPBUGS-87972 | Major | CF template TG attribute fix | +| SPLAT-307 | Closed/Done | Original NLB investigation (2021-2022) | +| SPLAT-443 | Closed | Phase 2 follow-up (never completed) | + +## Next Steps + +- Multiple iterations per scenario (configurable repeat count) +- Configurable delays via environment variables +- Per-second CSV output matching SPLAT-307 format +- CLB comparison variant (control group) +- JSON machine-readable report for cross-run comparison +- EC2 instance ID → node name mapping in observer events +- Scenario 5.6: node replacement (deregistration path) +- Scenario 5.7: connection termination regression guard (OCPBUGS-55626) +- Periodic CI job in CCCMO +- Multi-region runs (us-west-2, eu-west-1) From e107f3ea8fce3eb52726f1960f592f99b18aa0bd Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 02:41:34 -0300 Subject: [PATCH 09/22] e2e: add request statistics and per-phase breakdown to report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two new report sections for full traffic visibility: REQUEST STATISTICS: total request count with breakdown by HTTP status class (2xx, 4xx, 5xx) and connection errors. REQUEST BREAKDOWN BY PHASE: per-phase request counts (total, 2xx, errors, pre-readyz) across the test lifecycle phases: Warmup (t3→t5): all targets healthy, steady-state baseline Shutdown (t5→t7.1): readyz→503, target still serving Restart (t7.1→t9): pod deleted → new target healthy Recovery (t9→end): new target healthy, traffic flowing For Scenario 5.2: Shutdown (t5→t8), Recovery (t8→end). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../e2e/aws/lb_health_transition.go | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 199ff6bf3..13b2abe31 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -220,7 +220,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { report := buildReport("5.5 (Pre-Readyz Routing / OCPBUGS-86789)", tl, svcCfg, replicas, startupDelay, shutdownDelay, - allEvents, observer.Snapshots()) + allRecords, allEvents, observer.Snapshots()) if tl.PreReadyzReqCount > 0 { report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to pre-readyz target(s) — OCPBUGS-86789 reproduced\n", tl.PreReadyzReqCount) @@ -351,7 +351,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { report := buildReport("5.5-CAPA (Pre-Readyz + conn_term=false draining=300s)", tl, svcCfg, replicas, startupDelay, shutdownDelay, - allEvents, observer.Snapshots()) + allRecords, allEvents, observer.Snapshots()) if tl.PreReadyzReqCount > 0 { report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to pre-readyz target(s) — OCPBUGS-86789 reproduced (CAPA config)\n", tl.PreReadyzReqCount) @@ -439,7 +439,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { report := buildReport("5.2 (Shutdown Propagation / SPLAT-307)", tl, svcCfg, replicas, startupDelay, 0, - allEvents, observer.Snapshots()) + allRecords, allEvents, observer.Snapshots()) report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to unhealthy target after readyz→503\n", tl.UnhealthyReqCount) if !tl.T7.IsZero() && !tl.T5.IsZero() { @@ -872,6 +872,7 @@ func buildReport( cfg serviceConfig, replicas int32, startupDelay, shutdownDelay time.Duration, + records []health.RequestRecord, events []health.HealthEvent, snapshots []health.TargetSnapshot, ) string { @@ -952,6 +953,85 @@ func buildReport( w("%-25s %-14s %-14s %s", "T_route_start", fmtDur(tl.T8, tl.T10), "20-120s", "t10-t8: first req after readyz→200") w("%-25s %-14s %-14s %s", "T_total_cycle", fmtDur(tl.T5, tl.T10), "", "t10-t5: full cycle") + // ── Request statistics ── + // Compute overall and per-phase request counts from client records. + var totalReqs, reqs2xx, reqs4xx, reqs5xx, reqsErr int + for _, r := range records { + totalReqs++ + switch { + case r.Error != "": + reqsErr++ + case r.HTTPStatus >= 200 && r.HTTPStatus < 300: + reqs2xx++ + case r.HTTPStatus >= 400 && r.HTTPStatus < 500: + reqs4xx++ + case r.HTTPStatus >= 500: + reqs5xx++ + } + } + + w("") + w("REQUEST STATISTICS") + w(" Total: %d", totalReqs) + w(" 2xx: %d", reqs2xx) + w(" 4xx: %d", reqs4xx) + w(" 5xx: %d", reqs5xx) + w(" Errors: %d (connection/timeout failures)", reqsErr) + + // ── Per-phase request breakdown ── + // Phases are defined by the timeline milestones: + // Warmup: t3→t5 (all targets healthy, steady-state traffic) + // Shutdown: t5→t7.1 or t5→t8 (readyz→503, target still serving) + // Restart: t7.1→t9 (pod deleted → new target healthy) + // Recovery: t9→end (new target healthy, traffic flowing) + // For Scenario 5.2 (no restart): Shutdown=t5→t8, Recovery=t8→end + type phaseStats struct { + name string + total, ok, err, preRdz int + } + var phases []phaseStats + + classifyPhase := func(name string, from, to time.Time) phaseStats { + ps := phaseStats{name: name} + for _, r := range records { + if (!from.IsZero() && r.Timestamp.Before(from)) || (!to.IsZero() && r.Timestamp.After(to)) { + continue + } + ps.total++ + if r.Error != "" { + ps.err++ + } else if r.HTTPStatus >= 200 && r.HTTPStatus < 300 { + ps.ok++ + } + if r.IsNonReadyReq { + ps.preRdz++ + } + } + return ps + } + + // Warmup: t3 (all healthy) → t5 (readyz→503). Includes steady state. + phases = append(phases, classifyPhase("Warmup (t3→t5)", tl.T3, tl.T5)) + + if !tl.T71.IsZero() { + // Scenario 5.5: has restart phase + phases = append(phases, classifyPhase("Shutdown (t5→t7.1)", tl.T5, tl.T71)) + phases = append(phases, classifyPhase("Restart (t7.1→t9)", tl.T71, tl.T9)) + phases = append(phases, classifyPhase("Recovery (t9→end)", tl.T9, time.Time{})) + } else { + // Scenario 5.2: no restart + phases = append(phases, classifyPhase("Shutdown (t5→t8)", tl.T5, tl.T8)) + phases = append(phases, classifyPhase("Recovery (t8→end)", tl.T8, time.Time{})) + } + + w("") + w("REQUEST BREAKDOWN BY PHASE") + w("%-25s %8s %8s %8s %8s", "Phase", "Total", "2xx", "Errors", "PreRdz") + w("%-25s %8s %8s %8s %8s", strings.Repeat("─", 25), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8)) + for _, ps := range phases { + w("%-25s %8d %8d %8d %8d", ps.name, ps.total, ps.ok, ps.err, ps.preRdz) + } + // ── Unified chronological timeline ── w("") w("TIMELINE") From d5fa8dc9dc2c87faa5e323df7879fd209e893ce6 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 02:48:05 -0300 Subject: [PATCH 10/22] e2e: add duration column to per-phase request breakdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show how long each phase lasted alongside the request counts. Open-ended phases (Recovery→end) use the last recorded request timestamp to compute duration. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../e2e/aws/lb_health_transition.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 13b2abe31..12cd719e6 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -987,12 +987,13 @@ func buildReport( // For Scenario 5.2 (no restart): Shutdown=t5→t8, Recovery=t8→end type phaseStats struct { name string + from, to time.Time total, ok, err, preRdz int } var phases []phaseStats classifyPhase := func(name string, from, to time.Time) phaseStats { - ps := phaseStats{name: name} + ps := phaseStats{name: name, from: from, to: to} for _, r := range records { if (!from.IsZero() && r.Timestamp.Before(from)) || (!to.IsZero() && r.Timestamp.After(to)) { continue @@ -1010,26 +1011,30 @@ func buildReport( return ps } - // Warmup: t3 (all healthy) → t5 (readyz→503). Includes steady state. phases = append(phases, classifyPhase("Warmup (t3→t5)", tl.T3, tl.T5)) if !tl.T71.IsZero() { - // Scenario 5.5: has restart phase phases = append(phases, classifyPhase("Shutdown (t5→t7.1)", tl.T5, tl.T71)) phases = append(phases, classifyPhase("Restart (t7.1→t9)", tl.T71, tl.T9)) phases = append(phases, classifyPhase("Recovery (t9→end)", tl.T9, time.Time{})) } else { - // Scenario 5.2: no restart phases = append(phases, classifyPhase("Shutdown (t5→t8)", tl.T5, tl.T8)) phases = append(phases, classifyPhase("Recovery (t8→end)", tl.T8, time.Time{})) } w("") w("REQUEST BREAKDOWN BY PHASE") - w("%-25s %8s %8s %8s %8s", "Phase", "Total", "2xx", "Errors", "PreRdz") - w("%-25s %8s %8s %8s %8s", strings.Repeat("─", 25), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8)) + w("%-25s %10s %8s %8s %8s %8s", "Phase", "Duration", "Total", "2xx", "Errors", "PreRdz") + w("%-25s %10s %8s %8s %8s %8s", strings.Repeat("─", 25), strings.Repeat("─", 10), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8)) for _, ps := range phases { - w("%-25s %8d %8d %8d %8d", ps.name, ps.total, ps.ok, ps.err, ps.preRdz) + dur := "N/A" + if !ps.from.IsZero() && !ps.to.IsZero() { + dur = ps.to.Sub(ps.from).Truncate(time.Second).String() + } else if !ps.from.IsZero() && len(records) > 0 { + // Open-ended phase (→end): use last record timestamp + dur = records[len(records)-1].Timestamp.Sub(ps.from).Truncate(time.Second).String() + } + w("%-25s %10s %8d %8d %8d %8d", ps.name, dur, ps.total, ps.ok, ps.err, ps.preRdz) } // ── Unified chronological timeline ── From bf37dd977becafce9d33628609b502c22f81d1eb Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 02:48:42 -0300 Subject: [PATCH 11/22] e2e: restore removed comments in phase classification Co-Authored-By: Claude Opus 4.6 (1M context) --- openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 12cd719e6..cfb49fa33 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -1011,13 +1011,16 @@ func buildReport( return ps } + // Warmup: t3 (all healthy) → t5 (readyz→503). Includes steady state. phases = append(phases, classifyPhase("Warmup (t3→t5)", tl.T3, tl.T5)) if !tl.T71.IsZero() { + // Scenario 5.5: has restart phase phases = append(phases, classifyPhase("Shutdown (t5→t7.1)", tl.T5, tl.T71)) phases = append(phases, classifyPhase("Restart (t7.1→t9)", tl.T71, tl.T9)) phases = append(phases, classifyPhase("Recovery (t9→end)", tl.T9, time.Time{})) } else { + // Scenario 5.2: no restart phases = append(phases, classifyPhase("Shutdown (t5→t8)", tl.T5, tl.T8)) phases = append(phases, classifyPhase("Recovery (t8→end)", tl.T8, time.Time{})) } From 0b9194ee7fe69cecdc6838a03579e2a124b4ba14 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 03:05:06 -0300 Subject: [PATCH 12/22] e2e: parallel client workers, fix unhealthy.draining state matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Client throughput fix: Run 4 parallel request goroutines instead of 1 sequential worker. With ~1.8s RTT (e.g., South America → us-east-1), a single worker achieves only ~0.5 req/s regardless of ticker interval. 4 parallel workers each fire independently on their own 200ms ticker, giving ~2 req/s even on high-latency links. NewClient() now takes a numWorkers parameter. Each worker creates new TCP connections (DisableKeepAlives) independently. The shared records slice is protected by the existing mutex. CAPA state matching fix: When connection_termination.enabled=false (CAPA fix), the NLB TG transitions through "unhealthy.draining" instead of "unhealthy". Add isUnhealthyState() helper using strings.HasPrefix("unhealthy") to match both states. Applied to all state comparisons in timeline computation. This also confirms v5 open question Q5: unhealthy.draining occurs during HC-driven transitions with conn_term=false. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/client.go | 25 ++++++++++--- .../e2e/aws/lb_health_transition.go | 37 +++++++++++++------ 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go index 640b94f61..3a2afe95a 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go @@ -16,9 +16,14 @@ import ( // capturing per-request connection timing and server-reported state headers. // Each request uses a new TCP connection (DisableKeepAlives) to match NLB // per-connection routing behavior. +// +// Multiple worker goroutines run in parallel so that high-latency links +// (e.g., client in South America → NLB in us-east-1) don't bottleneck +// throughput — each worker fires independently on its own ticker. type Client struct { targetURL string interval time.Duration + workers int httpClient *http.Client mu sync.Mutex @@ -27,11 +32,19 @@ type Client struct { cancel context.CancelFunc } -// NewClient creates a Client that polls the given URL at the given interval. -func NewClient(targetURL string, interval time.Duration) *Client { +// NewClient creates a Client that polls the given URL at the given interval +// using numWorkers parallel goroutines. Each worker sends one request per +// interval tick independently, so effective throughput is approximately +// numWorkers / interval when RTT < interval, or numWorkers / RTT when +// RTT > interval. +func NewClient(targetURL string, interval time.Duration, numWorkers int) *Client { + if numWorkers < 1 { + numWorkers = 1 + } return &Client{ targetURL: targetURL, interval: interval, + workers: numWorkers, httpClient: &http.Client{ Transport: &http.Transport{ DisableKeepAlives: true, @@ -42,13 +55,15 @@ func NewClient(targetURL string, interval time.Duration) *Client { } } -// Start begins sending requests in a background goroutine. +// Start begins sending requests in background goroutines (one per worker). func (c *Client) Start(ctx context.Context) { ctx, c.cancel = context.WithCancel(ctx) - go c.pollLoop(ctx) + for i := 0; i < c.workers; i++ { + go c.pollLoop(ctx) + } } -// Stop cancels the background request goroutine. +// Stop cancels all background request goroutines. func (c *Client) Stop() { if c.cancel != nil { c.cancel() diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index cfb49fa33..ca1f626c7 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -34,10 +34,16 @@ const ( // the process exits. CKAO sets 135s; we add buffer for HC propagation. kasShutdownDelay = 192 * time.Second - // defaultClientInterval controls how often the HTTP client sends requests + // defaultClientInterval controls how often each worker sends requests // through the NLB. Lower values increase load density for propagation testing. defaultClientInterval = 200 * time.Millisecond + // defaultClientWorkers is the number of parallel request goroutines. + // Multiple workers prevent high-latency links (e.g., client in South America + // → NLB in us-east-1) from bottlenecking throughput. Each worker fires + // independently on its own ticker. + defaultClientWorkers = 4 + // postHealthyObserve is how long we continue observing after all targets // become healthy (both initial setup and post-restart). 90s gives enough // time to confirm stable routing while keeping test duration reasonable. @@ -133,9 +139,9 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { observer.Start(ctx) framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval, defaultClientWorkers) client.Start(ctx) - framework.Logf("[client] started sending requests to %s every %s", lbDNS, defaultClientInterval) + framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() // Steady state: 90s after all targets healthy — confirms stable @@ -277,9 +283,9 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { observer.Start(ctx) framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval, defaultClientWorkers) client.Start(ctx) - framework.Logf("[client] started sending requests to %s every %s", lbDNS, defaultClientInterval) + framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) @@ -388,9 +394,9 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { observer.Start(ctx) framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval) + client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval, defaultClientWorkers) client.Start(ctx) - framework.Logf("[client] started sending requests to %s every %s", lbDNS, defaultClientInterval) + framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) @@ -673,6 +679,13 @@ func waitForNewPod(ctx context.Context, cs clientset.Interface, namespace, deplo // ─── Timeline computation ─────────────────────────────────────────────────── +// isUnhealthyState returns true for any unhealthy TG state, including +// "unhealthy.draining" which occurs when connection_termination.enabled=false +// (CAPA fix / OCPBUGS-55626). +func isUnhealthyState(state string) bool { + return strings.HasPrefix(state, "unhealthy") +} + // computeTimeline builds the full timing model for Scenario 5.5 from raw // client records and observer events. See transitionTimeline for the t-value // definitions aligned with the SPLAT-307 state machine. @@ -692,7 +705,7 @@ func computeTimeline( if e.Timestamp.Before(t5) { continue } - if e.State == "unhealthy" && e.PrevState == "healthy" { + if isUnhealthyState(e.State) && e.PrevState == "healthy" { tl.T6 = e.Timestamp break } @@ -760,7 +773,7 @@ func computeTimeline( if e.Timestamp.Before(t71) { continue } - if e.State == "healthy" && (e.PrevState == "unhealthy" || e.PrevState == "initial") { + if e.State == "healthy" && (isUnhealthyState(e.PrevState) || e.PrevState == "initial") { tl.T9 = e.Timestamp break } @@ -787,7 +800,7 @@ func computeTimeline52( if e.Timestamp.Before(t5) { continue } - if e.State == "unhealthy" && e.PrevState == "healthy" { + if isUnhealthyState(e.State) && e.PrevState == "healthy" { tl.T6 = e.Timestamp break } @@ -811,7 +824,7 @@ func computeTimeline52( if e.Timestamp.Before(t8) { continue } - if e.State == "healthy" && e.PrevState == "unhealthy" { + if e.State == "healthy" && isUnhealthyState(e.PrevState) { tl.T9 = e.Timestamp break } @@ -908,7 +921,7 @@ func buildReport( if shutdownDelay > 0 { w(" Shutdown Delay: %s", shutdownDelay) } - w(" Client Interval: %s", defaultClientInterval) + w(" Client Interval: %s (%d parallel workers)", defaultClientInterval, defaultClientWorkers) // ── Service config ── w("") From 21bee9251a5f374de5d3747e2b510391b4c6b2b7 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 03:07:40 -0300 Subject: [PATCH 13/22] health: update README with parallel workers, request stats, CAPA finding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document parallel client workers (4 goroutines × 200ms ticker), request statistics and per-phase breakdown in report output, and the confirmed unhealthy.draining state finding for CAPA config. Update Scenario 5.5 flow to reflect current observation windows (90s post-healthy, event-driven) and client config (4 workers). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/README.md | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md index 09aa5fabc..827c12bb5 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md @@ -112,14 +112,14 @@ Test name (OTE): Flow: 1. Deploy 3 healthserver pods on control-plane nodes (--startup-delay=60s) 2. Create NLB: control-plane-only targets, cross-zone, HTTP /readyz HC - 3. Wait for ALL TG targets healthy - 4. Start observer + client (200ms interval, new TCP per request) - 5. Observe 90s steady state (all replicas receiving traffic) + 3. Wait for ALL TG targets healthy (zero initial/unhealthy) + 4. Start observer (1s) + client (200ms × 4 parallel workers) + 5. Observe 90s steady state (confirm all replicas receiving traffic) 6. Signal target pod readyz→503 via K8s API proxy (t5) 7. Wait 192s shutdown-delay (simulates KAS shutdown-delay-duration) 8. Delete pod (t7.1), wait for replacement 9. Wait for restarted target healthy + 90s post-recovery observation - 10. Report full t0–t10 timing table + unified chronological timeline + 10. Report: timing table, request stats, phase breakdown, timeline Detection: any response with X-Server-State: pre-readyz = BUG reproduced ``` @@ -137,6 +137,12 @@ target_health_state.unhealthy.draining_interval_seconds = 300 These are the CAPA fix attributes (OCPBUGS-55626). Tests whether they affect the unhealthy→healthy routing transition. +**Finding (confirmed):** with `connection_termination.enabled=false`, the +NLB TG transitions through `unhealthy.draining` instead of `unhealthy`. +This answers v5 open question Q5 — `unhealthy.draining` occurs during +HC-driven transitions, not just during deregistration. The timeline +computation uses `isUnhealthyState()` to match both states. + ### Scenario 5.2 — Shutdown Propagation (SPLAT-307) Measures how long the NLB routes to a target after `/readyz` fails (no pod @@ -200,6 +206,12 @@ Also provides: HTTP client with `httptrace` hooks. Creates a **new TCP connection** per request (`DisableKeepAlives`) to match NLB per-connection routing. +Runs **multiple parallel worker goroutines** (default: 4) so that +high-latency links (e.g., test runner in South America → NLB in us-east-1) +don't bottleneck throughput. Each worker fires independently on its own +200ms ticker. With ~1.8s RTT, 4 workers achieve ~2 req/s; closer to +the cluster, the same config gives ~20 req/s. + Captures per request: target IP, TCP dial duration, HTTP status, `X-Server-State`, `X-Server-ID`, `X-First-Readyz-Time`. @@ -289,10 +301,14 @@ avoid per-line logger timestamps) containing: - **ENVIRONMENT**: platform, region, topology - **TARGET**: pod name, node, new pod (if restart) - **TEST PARAMETERS**: replicas, startup/shutdown delay, client interval + and worker count - **SERVICE CONFIGURATION**: LB DNS/ARN, all Service annotations - **TARGET GROUP CONFIGURATION**: TG ARN, target type, all TG attributes including health check config -- **TIMING TABLE**: all computed metrics with expected values +- **TIMING TABLE**: all computed metrics (t0–t10) with expected values +- **REQUEST STATISTICS**: total count, 2xx/4xx/5xx breakdown, errors +- **REQUEST BREAKDOWN BY PHASE**: per-phase (Warmup, Shutdown, Restart, + Recovery) duration, request count, 2xx, errors, pre-readyz count - **TIMELINE**: chronological merge of test milestones (t0–t10) and TG health events, full RFC3339 timestamps with deltas - **TG SNAPSHOTS**: first/last poll with healthy/unhealthy/initial counts From 0e80b33b03c514e9dc9fe27192f426c502735d2f Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 03:45:11 -0300 Subject: [PATCH 14/22] e2e: add per-server request distribution and server-side verdict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add PER-SERVER REQUEST DISTRIBUTION BY PHASE section to the report, showing how many requests each backend (pod/ServerID) received in each phase. Servers are annotated with their role (← TARGET for the pod being rolled out, ← NEW for the replacement pod). This reveals whether the NLB is correctly routing away from the unhealthy target during Shutdown and Restart phases. Restructure the verdict into dedicated buildVerdict55/buildVerdict52 functions that check both client-side and server-side metrics: Scenario 5.5 verdict checks: - [BUG] Pre-readyz requests (X-Server-State header) - [SHUTDOWN] Requests to target pod after readyz→503 - [RESTART] Unhealthy/pre-readyz requests on target node during Restart phase (t7.1→t9) - [OK] No pre-readyz routing when all checks pass Scenario 5.2 verdict shows: - Unhealthy request count and T_route_stop - T_route_start for recovery measurement The per-server view makes it immediately clear whether the 2xx requests during Shutdown/Restart phases went to healthy backends (expected) or to the target pod (the NLB bug). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../e2e/aws/lb_health_transition.go | 196 ++++++++++++++++-- 1 file changed, 181 insertions(+), 15 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index ca1f626c7..7ac5530de 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -228,11 +228,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { tl, svcCfg, replicas, startupDelay, shutdownDelay, allRecords, allEvents, observer.Snapshots()) - if tl.PreReadyzReqCount > 0 { - report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to pre-readyz target(s) — OCPBUGS-86789 reproduced\n", tl.PreReadyzReqCount) - } else { - report += "\nVERDICT: No pre-readyz routing detected in this iteration\n" - } + report += buildVerdict55(tl, allRecords) framework.Logf("\n%s", report) }) @@ -359,11 +355,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { tl, svcCfg, replicas, startupDelay, shutdownDelay, allRecords, allEvents, observer.Snapshots()) - if tl.PreReadyzReqCount > 0 { - report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to pre-readyz target(s) — OCPBUGS-86789 reproduced (CAPA config)\n", tl.PreReadyzReqCount) - } else { - report += "\nVERDICT: No pre-readyz routing detected with CAPA TG attributes\n" - } + report += buildVerdict55(tl, allRecords) framework.Logf("\n%s", report) }) @@ -447,11 +439,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { tl, svcCfg, replicas, startupDelay, 0, allRecords, allEvents, observer.Snapshots()) - report += fmt.Sprintf("\nVERDICT: NLB routed %d request(s) to unhealthy target after readyz→503\n", tl.UnhealthyReqCount) - if !tl.T7.IsZero() && !tl.T5.IsZero() { - report += fmt.Sprintf("T_route_stop = %s (NLB kept routing after readyz→503)\n", - tl.T7.Sub(tl.T5).Truncate(time.Second)) - } + report += buildVerdict52(tl) framework.Logf("\n%s", report) }) @@ -1053,6 +1041,86 @@ func buildReport( w("%-25s %10s %8d %8d %8d %8d", ps.name, dur, ps.total, ps.ok, ps.err, ps.preRdz) } + // ── Per-server request distribution by phase ── + // Shows how many requests each backend (ServerID/pod) received in each phase. + // This is the key metric for detecting routing anomalies: if the target pod + // receives requests during Restart (after deletion), that's the NLB bug. + // Collect unique server IDs across all records + serverSet := make(map[string]bool) + for _, r := range records { + if r.ServerID != "" { + serverSet[r.ServerID] = true + } + } + var serverIDs []string + for id := range serverSet { + serverIDs = append(serverIDs, id) + } + sort.Strings(serverIDs) + + if len(serverIDs) > 0 { + // Build per-server per-phase counts + type serverPhaseCount struct { + total, preRdz int + } + // phaseServerCounts[phaseIdx][serverID] = counts + phaseServerCounts := make([]map[string]serverPhaseCount, len(phases)) + for i, ps := range phases { + phaseServerCounts[i] = make(map[string]serverPhaseCount) + for _, r := range records { + if r.ServerID == "" { + continue + } + if (!ps.from.IsZero() && r.Timestamp.Before(ps.from)) || (!ps.to.IsZero() && r.Timestamp.After(ps.to)) { + continue + } + sc := phaseServerCounts[i][r.ServerID] + sc.total++ + if r.IsNonReadyReq { + sc.preRdz++ + } + phaseServerCounts[i][r.ServerID] = sc + } + } + + w("") + w("PER-SERVER REQUEST DISTRIBUTION BY PHASE") + + // Annotate server IDs with their role in the test + serverLabel := func(id string) string { + switch id { + case tl.TargetPod: + return id + " ← TARGET" + case tl.NewPod: + return id + " ← NEW" + default: + return id + } + } + + // Print a sub-table per phase showing each server's request count + for i, ps := range phases { + dur := "N/A" + if !ps.from.IsZero() && !ps.to.IsZero() { + dur = ps.to.Sub(ps.from).Truncate(time.Second).String() + } else if !ps.from.IsZero() && len(records) > 0 { + dur = records[len(records)-1].Timestamp.Sub(ps.from).Truncate(time.Second).String() + } + w(" %s (%s):", ps.name, dur) + for _, sid := range serverIDs { + sc := phaseServerCounts[i][sid] + if sc.total == 0 { + continue + } + preRdzNote := "" + if sc.preRdz > 0 { + preRdzNote = fmt.Sprintf(" ← %d pre-readyz!", sc.preRdz) + } + w(" %-50s reqs=%d%s", serverLabel(sid), sc.total, preRdzNote) + } + } + } + // ── Unified chronological timeline ── w("") w("TIMELINE") @@ -1119,6 +1187,104 @@ func buildReport( // ─── Resource builders ────────────────────────────────────────────────────── +// ─── Verdict builders ─────────────────────────────────────────────────────── + +// buildVerdict55 produces the verdict string for Scenario 5.5 (pre-readyz routing). +// It checks both client-side (X-Server-State: pre-readyz) and server-side +// (did the target pod receive requests during Shutdown/Restart phases). +func buildVerdict55(tl transitionTimeline, records []health.RequestRecord) string { + var b strings.Builder + w := func(format string, args ...any) { fmt.Fprintf(&b, format+"\n", args...) } + + // Count requests to the target pod AFTER readyz→503 (shutdown phase) + var targetAfterShutdown int + for _, r := range records { + if r.Timestamp.Before(tl.T5) || r.ServerID != tl.TargetPod { + continue + } + targetAfterShutdown++ + } + + // Count requests to the target pod's node during Restart phase (t7.1→t9). + // With externalTrafficPolicy: Local, instance target type, the target pod's + // node is the NLB target. Any request reaching that node's backend during + // restart means the NLB routed to an unhealthy target. + var targetDuringRestart int + if !tl.T71.IsZero() { + end := tl.T9 + if end.IsZero() { + end = tl.T10 + } + for _, r := range records { + if tl.T71.IsZero() || r.Timestamp.Before(tl.T71) { + continue + } + if !end.IsZero() && r.Timestamp.After(end) { + continue + } + // Match the target pod OR the new pod (both run on the same node + // when the deployment reschedules to the same node) + if r.ServerID == tl.TargetPod || r.ServerID == tl.NewPod { + if r.ServerState == "pre-readyz" || r.ServerState == "draining" || r.ServerState == "shutdown" { + targetDuringRestart++ + } + } + } + } + + w("") + w("VERDICT") + + if tl.PreReadyzReqCount > 0 { + w(" [BUG] NLB routed %d request(s) with X-Server-State: pre-readyz", tl.PreReadyzReqCount) + w(" This reproduces OCPBUGS-86789 — NLB routes before /readyz passes") + } + + if targetAfterShutdown > 0 { + w(" [SHUTDOWN] Target pod received %d request(s) after readyz→503 (T_route_stop=%s)", + targetAfterShutdown, fmtDur(tl.T5, tl.T7)) + w(" NLB continued routing to unhealthy target for %s", fmtDur(tl.T5, tl.T7)) + } + + if targetDuringRestart > 0 { + w(" [RESTART] Target node received %d unhealthy/pre-readyz request(s) during Restart phase", targetDuringRestart) + } + + if tl.PreReadyzReqCount == 0 && targetDuringRestart == 0 { + w(" [OK] No pre-readyz routing detected") + w(" NLB correctly waited for HC to pass before routing to restarted target") + } + + if targetAfterShutdown > 0 { + w(" [INFO] Shutdown propagation: %d requests routed to target after readyz→503 (expected: NLB propagation delay)", + targetAfterShutdown) + } + + return b.String() +} + +// buildVerdict52 produces the verdict string for Scenario 5.2 (shutdown propagation). +func buildVerdict52(tl transitionTimeline) string { + var b strings.Builder + w := func(format string, args ...any) { fmt.Fprintf(&b, format+"\n", args...) } + + w("") + w("VERDICT") + w(" NLB routed %d request(s) to unhealthy target after readyz→503", tl.UnhealthyReqCount) + if !tl.T7.IsZero() && !tl.T5.IsZero() { + w(" T_route_stop = %s (NLB kept routing after readyz→503)", + tl.T7.Sub(tl.T5).Truncate(time.Second)) + } + if !tl.T10.IsZero() && !tl.T8.IsZero() { + w(" T_route_start = %s (NLB started routing after readyz→200)", + tl.T10.Sub(tl.T8).Truncate(time.Second)) + } + + return b.String() +} + +// ─── Resource builders ────────────────────────────────────────────────────── + // buildHealthserverDeployment creates a Deployment spec that schedules pods on // master/control-plane nodes to match KAS topology. Includes tolerations for // both master and control-plane taints, and topologySpreadConstraints to From 16bde17781383251a2c9f63a8260f06ec3675bd7 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 6 Aug 2026 03:47:25 -0300 Subject: [PATCH 15/22] health: update README with per-server distribution and verdict sections Document the PER-SERVER REQUEST DISTRIBUTION BY PHASE report section and the multi-signal verdict logic (BUG/SHUTDOWN/RESTART/OK checks). Co-Authored-By: Claude Opus 4.6 (1M context) --- openshift-tests/ccm-aws-tests/e2e/aws/health/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md index 827c12bb5..bdfe0f7bc 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md @@ -309,10 +309,15 @@ avoid per-line logger timestamps) containing: - **REQUEST STATISTICS**: total count, 2xx/4xx/5xx breakdown, errors - **REQUEST BREAKDOWN BY PHASE**: per-phase (Warmup, Shutdown, Restart, Recovery) duration, request count, 2xx, errors, pre-readyz count +- **PER-SERVER REQUEST DISTRIBUTION BY PHASE**: how many requests each + backend (pod) received in each phase, annotated with role (TARGET, NEW). + Shows whether the NLB correctly routed away from the unhealthy target. - **TIMELINE**: chronological merge of test milestones (t0–t10) and TG health events, full RFC3339 timestamps with deltas - **TG SNAPSHOTS**: first/last poll with healthy/unhealthy/initial counts -- **VERDICT**: detection result +- **VERDICT**: multi-signal detection — checks pre-readyz header (client), + target pod requests after readyz→503 (server shutdown), and unhealthy + requests on target node during restart (server restart) ## Related Issues From 990a4cbbbbf39f2ed66190cf2570eeb1d4f57500 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Wed, 12 Aug 2026 16:29:15 -0300 Subject: [PATCH 16/22] =?UTF-8?q?e2e:=20tune=20workers=20to=208=C3=97100ms?= =?UTF-8?q?,=20add=20pod=E2=86=92node=20mapping,=20req/s=20metrics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Client tuning based on empirical testing (South America → us-east-1): 4×200ms = 9.3 req/s, 0.5% errors (baseline) 8×100ms = 12.8 req/s, 2.1% errors (sweet spot — selected) 16×50ms = 13.6 req/s, 5.3% errors (diminishing returns) 40×50ms = port exhaustion / K8s API timeout (broken) RTT (~430ms) is the bottleneck, not worker count. Higher concurrency with DisableKeepAlives just creates more TCP connections competing on the same network path. Add pod→node name mapping to PER-SERVER REQUEST DISTRIBUTION: healthserver-abc (ip-10-0-22-243.ec2.internal) ← TARGET reqs=399 Shows which node each pod runs on, making it clear when the target pod is deleted and a new pod appears on the same or different node. PodNodeMap is populated from pod list at setup + new pod Get after replacement, stored in transitionTimeline. Add throughput metrics: - REQUEST STATISTICS: overall avg req/s and test duration - REQUEST BREAKDOWN BY PHASE: per-phase avg req/s column Add better error context to all pod list calls. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/e2e/aws/health/client.go | 3 + .../e2e/aws/lb_health_transition.go | 110 ++++++++++++++---- 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go index 3a2afe95a..c2f17e113 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/client.go @@ -47,6 +47,9 @@ func NewClient(targetURL string, interval time.Duration, numWorkers int) *Client workers: numWorkers, httpClient: &http.Client{ Transport: &http.Transport{ + // DisableKeepAlives forces a new TCP connection per request, + // matching NLB per-connection routing behavior. This is + // essential for detecting which target receives each request. DisableKeepAlives: true, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 7ac5530de..9087cc734 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -35,14 +35,20 @@ const ( kasShutdownDelay = 192 * time.Second // defaultClientInterval controls how often each worker sends requests - // through the NLB. Lower values increase load density for propagation testing. - defaultClientInterval = 200 * time.Millisecond - - // defaultClientWorkers is the number of parallel request goroutines. - // Multiple workers prevent high-latency links (e.g., client in South America - // → NLB in us-east-1) from bottlenecking throughput. Each worker fires - // independently on its own ticker. - defaultClientWorkers = 4 + // through the NLB. Each worker fires independently on its own ticker. + // With DisableKeepAlives (new TCP per request), each worker creates + // one outbound connection at a time. Too many workers with short + // intervals can exhaust ephemeral ports and starve K8s API calls. + // 8 workers at 100ms: best throughput/error ratio from testing. + // Tested configurations (South America → us-east-1, ~430ms RTT): + // 4×200ms = 9.3 req/s, 0.5% errors (baseline) + // 8×100ms = 12.8 req/s, 2.1% errors (sweet spot) + // 16×50ms = 13.6 req/s, 5.3% errors (diminishing returns) + // 40×50ms = port exhaustion / API timeout (broken) + // RTT is the bottleneck, not worker count. More workers from same + // machine just create more connections on the same network path. + defaultClientInterval = 100 * time.Millisecond + defaultClientWorkers = 8 // postHealthyObserve is how long we continue observing after all targets // become healthy (both initial setup and post-restart). 90s gives enough @@ -85,6 +91,10 @@ type transitionTimeline struct { TargetPod string TargetNode string NewPod string + + // PodNodeMap maps pod names to the node they run on, used for + // displaying node identity alongside pod names in the report. + PodNodeMap map[string]string } // serviceConfig records Service, TG, and environment configuration for the report. @@ -159,17 +169,19 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { framework.Logf("[steady] %d requests, %d non-ready", len(steadyRecords), steadyNonReady) Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + By("listing pods to identify target for rollout simulation") pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployName), }) - framework.ExpectNoError(err) + framework.ExpectNoError(err, "list healthserver pods") Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) - // Build knownServers from ALL existing pods (not client records, - // which may miss pods due to NLB routing distribution). + // Build knownServers and podNodeMap from ALL existing pods. knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) for _, p := range pods.Items { knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName } targetPod := pods.Items[0].Name @@ -195,6 +207,12 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By("waiting for replacement pod") newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) + // Capture the new pod's node for the report + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + } + // Wait for the restarted target to become healthy again, then observe // for postHealthyObserve to confirm stable routing. By("waiting for restarted target to become healthy") @@ -208,12 +226,10 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { allEvents := observer.Events() tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) - // Copy setup-phase timers (t0-t3) into the timeline tl.T0 = setupTimes.T0 tl.T1 = setupTimes.T1 tl.T2 = setupTimes.T2 tl.T3 = setupTimes.T3 - // t4: first successful client request (NLB routing established) for _, r := range steadyRecords { if r.Error == "" && r.HTTPStatus > 0 { tl.T4 = r.Timestamp @@ -223,6 +239,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { tl.TargetPod = targetPod tl.TargetNode = targetNode tl.NewPod = newPod + tl.PodNodeMap = podNodeMap report := buildReport("5.5 (Pre-Readyz Routing / OCPBUGS-86789)", tl, svcCfg, replicas, startupDelay, shutdownDelay, @@ -296,15 +313,19 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { } Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + By("listing pods to identify target for rollout simulation") pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployName), }) - framework.ExpectNoError(err) + framework.ExpectNoError(err, "list healthserver pods (K8s API may be overloaded by client workers)") Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + // Build knownServers and podNodeMap from ALL existing pods. knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) for _, p := range pods.Items { knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName } targetPod := pods.Items[0].Name @@ -326,6 +347,12 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By("waiting for replacement pod") newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) + // Capture the new pod's node for the report + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + } + By("waiting for restarted target to become healthy") err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) framework.ExpectNoError(err, "restarted target healthy") @@ -350,6 +377,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { tl.TargetPod = targetPod tl.TargetNode = targetNode tl.NewPod = newPod + tl.PodNodeMap = podNodeMap report := buildReport("5.5-CAPA (Pre-Readyz + conn_term=false draining=300s)", tl, svcCfg, replicas, startupDelay, shutdownDelay, @@ -394,10 +422,15 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) time.Sleep(postHealthyObserve) + By("listing pods to identify target for shutdown simulation") pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployName), }) - framework.ExpectNoError(err) + framework.ExpectNoError(err, "list healthserver pods") + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + podNodeMap[p.Name] = p.Spec.NodeName + } targetPod := pods.Items[0].Name targetNode := pods.Items[0].Spec.NodeName @@ -434,6 +467,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { } tl.TargetPod = targetPod tl.TargetNode = targetNode + tl.PodNodeMap = podNodeMap report := buildReport("5.2 (Shutdown Propagation / SPLAT-307)", tl, svcCfg, replicas, startupDelay, 0, @@ -971,6 +1005,16 @@ func buildReport( } } + // Compute average req/s across the full test duration (t3→last record) + var avgReqsPerSec float64 + var testDuration time.Duration + if len(records) > 1 { + testDuration = records[len(records)-1].Timestamp.Sub(records[0].Timestamp) + if testDuration > 0 { + avgReqsPerSec = float64(totalReqs) / testDuration.Seconds() + } + } + w("") w("REQUEST STATISTICS") w(" Total: %d", totalReqs) @@ -978,6 +1022,8 @@ func buildReport( w(" 4xx: %d", reqs4xx) w(" 5xx: %d", reqs5xx) w(" Errors: %d (connection/timeout failures)", reqsErr) + w(" Duration: %s", testDuration.Truncate(time.Second)) + w(" Avg rate: %.1f req/s", avgReqsPerSec) // ── Per-phase request breakdown ── // Phases are defined by the timeline milestones: @@ -1028,17 +1074,23 @@ func buildReport( w("") w("REQUEST BREAKDOWN BY PHASE") - w("%-25s %10s %8s %8s %8s %8s", "Phase", "Duration", "Total", "2xx", "Errors", "PreRdz") - w("%-25s %10s %8s %8s %8s %8s", strings.Repeat("─", 25), strings.Repeat("─", 10), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8)) + w("%-25s %10s %8s %8s %8s %8s %10s", "Phase", "Duration", "Total", "2xx", "Errors", "PreRdz", "Avg req/s") + w("%-25s %10s %8s %8s %8s %8s %10s", strings.Repeat("─", 25), strings.Repeat("─", 10), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 8), strings.Repeat("─", 10)) for _, ps := range phases { dur := "N/A" + rps := "N/A" + var phaseDur time.Duration if !ps.from.IsZero() && !ps.to.IsZero() { - dur = ps.to.Sub(ps.from).Truncate(time.Second).String() + phaseDur = ps.to.Sub(ps.from) } else if !ps.from.IsZero() && len(records) > 0 { // Open-ended phase (→end): use last record timestamp - dur = records[len(records)-1].Timestamp.Sub(ps.from).Truncate(time.Second).String() + phaseDur = records[len(records)-1].Timestamp.Sub(ps.from) + } + if phaseDur > 0 { + dur = phaseDur.Truncate(time.Second).String() + rps = fmt.Sprintf("%.1f", float64(ps.total)/phaseDur.Seconds()) } - w("%-25s %10s %8d %8d %8d %8d", ps.name, dur, ps.total, ps.ok, ps.err, ps.preRdz) + w("%-25s %10s %8d %8d %8d %8d %10s", ps.name, dur, ps.total, ps.ok, ps.err, ps.preRdz, rps) } // ── Per-server request distribution by phase ── @@ -1086,16 +1138,24 @@ func buildReport( w("") w("PER-SERVER REQUEST DISTRIBUTION BY PHASE") - // Annotate server IDs with their role in the test + // Annotate server IDs with their role and node name. + // Format: "pod-name (node-name) ← TARGET" serverLabel := func(id string) string { + node := "" + if tl.PodNodeMap != nil { + node = tl.PodNodeMap[id] + } + role := "" switch id { case tl.TargetPod: - return id + " ← TARGET" + role = " ← TARGET" case tl.NewPod: - return id + " ← NEW" - default: - return id + role = " ← NEW" + } + if node != "" { + return fmt.Sprintf("%s (%s)%s", id, node, role) } + return id + role } // Print a sub-table per phase showing each server's request count From 199f306d6abbf351dd17691b4391fc22d75e31ac Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Wed, 12 Aug 2026 17:32:09 -0300 Subject: [PATCH 17/22] e2e: hostNetwork mode, SIGTERM-based shutdown, privileged SCC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to hostNetwork: true to match KAS static pod behavior. The NLB health check now hits nodeIP:19443/readyz directly — no kube-proxy mediation in the HC path. Port 19443 chosen to avoid conflicts on control-plane nodes (verified via netstat). hostPort declared for NLB HC access. Replace admin signal (HTTP POST via API proxy) with pod deletion + SIGTERM. The healthserver SIGTERM handler now sets state to "draining" (readyz→503) but keeps serving — matching KAS behavior during shutdown-delay-duration. terminationGracePeriodSeconds=192 gives the pod time to serve while draining. Grant privileged SCC to the default SA in the test namespace via RoleBinding to system:openshift:scc:privileged. Required for hostNetwork + hostPort on OpenShift. Skip Scenario 5.2 with hostNetwork (admin signal unreachable). Client tuning: 8 workers at 100ms (12.8 req/s sweet spot). Add req/s metrics and pod→node mapping to report. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ccm-aws-tests/cmd/healthserver/main.go | 18 ++- .../e2e/aws/lb_health_transition.go | 141 +++++++++++++----- 2 files changed, 117 insertions(+), 42 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go b/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go index fee0890db..458f2ff2a 100644 --- a/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go +++ b/openshift-tests/ccm-aws-tests/cmd/healthserver/main.go @@ -1,7 +1,6 @@ package main import ( - "context" "encoding/json" "flag" "fmt" @@ -75,19 +74,26 @@ func main() { s.mu.Unlock() }() + // On SIGTERM (sent by kubelet during pod deletion), transition to + // "draining" state: /readyz returns 503 but the server keeps serving + // on / with X-Server-State: draining. The pod continues to serve + // for terminationGracePeriodSeconds (set by the Deployment) before + // kubelet force-kills it. This matches KAS behavior during rollouts: + // SIGTERM → readyz→503 → keep serving for shutdown-delay-duration. go func() { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) sig := <-ch - log.Printf("received %s, shutting down", sig) + log.Printf("received %s, setting readyz→503 (draining), server continues serving", sig) s.mu.Lock() now := time.Now() s.shutdownInitiated = &now - s.state = stateShutdown + s.readyzFalseAt = &now + s.state = stateDraining s.mu.Unlock() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - srv.Shutdown(ctx) + // Do NOT call srv.Shutdown() — keep serving until kubelet kills us + // at the end of terminationGracePeriodSeconds. This simulates KAS + // keeping its TCP port open during shutdown-delay-duration. }() s.mu.Lock() diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 9087cc734..b54976120 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -16,6 +16,7 @@ import ( "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/common" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/wait" @@ -29,6 +30,11 @@ const ( healthTransitionTestPrefix = e2eTestPrefixLoadBalancer + " health-transition" + // healthserverPort is the port the healthserver binds on the node IP + // via hostNetwork. Chosen to avoid conflicts with existing services on + // control-plane nodes (verified via netstat). Echoes 6443 (KAS port). + healthserverPort = 19443 + // kasShutdownDelay matches the KAS shutdown-delay-duration (135s graceful + // margin), simulating how long KAS keeps serving after /readyz→503 before // the process exits. CKAO sets 135s; we add buffer for HC propagation. @@ -149,7 +155,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { observer.Start(ctx) framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval, defaultClientWorkers) + client := health.NewClient(fmt.Sprintf("http://%s:%d/", lbDNS, healthserverPort), defaultClientInterval, defaultClientWorkers) client.Start(ctx) framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() @@ -187,20 +193,15 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { targetPod := pods.Items[0].Name targetNode := pods.Items[0].Spec.NodeName - // t5: Signal readyz→503 — simulates KAS receiving SIGTERM - By("signaling target pod readyz→503 (t5)") + // t5 = t7.1: Delete pod — kubelet sends SIGTERM, healthserver sets + // readyz→503 and keeps serving for terminationGracePeriodSeconds (192s). + // This exactly matches KAS rollout behavior: SIGTERM → readyz→503 → + // keep serving for shutdown-delay-duration → process killed. + // After terminationGracePeriodSeconds, kubelet kills the pod and + // the Deployment creates a replacement. + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") t5 := time.Now() - err = sendAdminSignal(ctx, cs, ns.Name, targetPod, false) - framework.ExpectNoError(err, "signal readyz→false") - - // Wait shutdown-delay — simulates KAS shutdown-delay-duration (192s) - // during which the pod keeps serving but /readyz returns 503 - By(fmt.Sprintf("waiting %s shutdown-delay before pod deletion", shutdownDelay)) - time.Sleep(shutdownDelay) - - // t7.1: Delete pod — simulates KAS process exit - By("deleting target pod (t7.1)") - t71 := time.Now() + t71 := t5 err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) framework.ExpectNoError(err) @@ -296,7 +297,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { observer.Start(ctx) framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval, defaultClientWorkers) + client := health.NewClient(fmt.Sprintf("http://%s:%d/", lbDNS, healthserverPort), defaultClientInterval, defaultClientWorkers) client.Start(ctx) framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() @@ -331,16 +332,9 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { targetPod := pods.Items[0].Name targetNode := pods.Items[0].Spec.NodeName - By("signaling target pod readyz→503 (t5)") + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") t5 := time.Now() - err = sendAdminSignal(ctx, cs, ns.Name, targetPod, false) - framework.ExpectNoError(err, "signal readyz→false") - - By(fmt.Sprintf("waiting %s shutdown-delay before pod deletion", shutdownDelay)) - time.Sleep(shutdownDelay) - - By("deleting target pod (t7.1)") - t71 := time.Now() + t71 := t5 err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) framework.ExpectNoError(err) @@ -394,6 +388,13 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { It("should stop routing within shutdown-delay after "+ "readyz starts failing", func(ctx context.Context) { + // Scenario 5.2 requires the admin signal (readyz→503 without pod + // deletion) which doesn't work with hostNetwork: the K8s API server + // pod proxy can't reach nodeIP:19443 due to security group rules. + // TODO: implement alternative signaling (e.g., ConfigMap watch, or + // a non-hostNetwork admin sidecar). + Skip("Scenario 5.2 not yet supported with hostNetwork (admin signal unreachable)") + image := os.Getenv(envHealthserverImage) if image == "" { Skip(fmt.Sprintf("%s not set", envHealthserverImage)) @@ -414,7 +415,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { observer.Start(ctx) framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s/", lbDNS), defaultClientInterval, defaultClientWorkers) + client := health.NewClient(fmt.Sprintf("http://%s:%d/", lbDNS, healthserverPort), defaultClientInterval, defaultClientWorkers) client.Start(ctx) framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) defer func() { client.Stop(); observer.Stop() }() @@ -496,8 +497,14 @@ func setupHealthTransition( startupDelay time.Duration, ) (lbDNS string, observer *health.Observer, cfg serviceConfig, setupTimes transitionTimeline) { + // Grant the default SA in this namespace permission to use hostNetwork + // via the OpenShift hostnetwork-v2 SCC. Required because the healthserver + // pod uses hostNetwork: true to match KAS static pod behavior. + By("granting hostnetwork-v2 SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + // t0: deployment created — pods begin scheduling on master nodes - By("creating healthserver Deployment (scheduled on master nodes)") + By("creating healthserver Deployment (scheduled on master nodes, hostNetwork)") deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image) setupTimes.T0 = time.Now() _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) @@ -657,19 +664,26 @@ func fetchTGHealthCheckConfig(ctx context.Context, cfg *serviceConfig) { // ─── Admin API via K8s API server proxy ───────────────────────────────────── -// sendAdminSignal sends a readyz control signal to a healthserver pod via the -// K8s API server pod proxy endpoint. This avoids the need for port-forward -// or exec (the healthserver container is FROM scratch, no shell). +// sendAdminSignal sends a readyz control signal to a healthserver pod via +// the K8s API server pod proxy. With hostNetwork: true, the pod listens on +// the node's IP on healthserverPort. The API server proxy connects to +// podIP:port which equals nodeIP:port — this requires the API server to be +// able to reach the node on that port (same-node for control-plane pods). func sendAdminSignal(ctx context.Context, cs clientset.Interface, namespace, podName string, ready bool) error { readyStr := "false" if ready { readyStr = "true" } result := cs.CoreV1().RESTClient().Post(). - AbsPath(fmt.Sprintf("/api/v1/namespaces/%s/pods/%s:8080/proxy/admin/readyz", namespace, podName)). + AbsPath(fmt.Sprintf("/api/v1/namespaces/%s/pods/%s:%d/proxy/admin/readyz", namespace, podName, healthserverPort)). Param("ready", readyStr). + Timeout(30 * time.Second). Do(ctx) - return result.Error() + if err := result.Error(); err != nil { + return fmt.Errorf("admin signal ready=%s to %s: %w", readyStr, podName, err) + } + framework.Logf("[admin] sent readyz=%s to pod %s", readyStr, podName) + return nil } // ─── Pod lifecycle helpers ────────────────────────────────────────────────── @@ -1362,11 +1376,21 @@ func buildHealthserverDeployment(namespace, name string, replicas int32, startup Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{Labels: labels}, Spec: v1.PodSpec{ + // hostNetwork: pod binds directly on the node's network + // interface, exactly like KAS static pods. The NLB health + // check hits nodeIP:19443/readyz directly — no kube-proxy + // mediation. This is essential for reproducing OCPBUGS-86789. + HostNetwork: true, + DNSPolicy: v1.DNSClusterFirstWithHostNet, // Schedule on control-plane nodes to match KAS topology. // OCP 5.x uses control-plane; OCP 4.x has both labels. NodeSelector: map[string]string{ "node-role.kubernetes.io/control-plane": "", }, + // terminationGracePeriodSeconds matches KAS + // shutdown-delay-duration. After SIGTERM, the healthserver + // sets readyz→503 and keeps serving for this duration. + TerminationGracePeriodSeconds: ptrInt64(int64(kasShutdownDelay.Seconds())), // Tolerate master and control-plane taints Tolerations: []v1.Toleration{ {Key: "node-role.kubernetes.io/master", Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoSchedule}, @@ -1381,11 +1405,26 @@ func buildHealthserverDeployment(namespace, name string, replicas int32, startup Containers: []v1.Container{{ Name: "healthserver", Image: image, - Args: []string{fmt.Sprintf("--startup-delay=%s", startupDelay)}, + Args: []string{ + fmt.Sprintf("--port=%d", healthserverPort), + fmt.Sprintf("--startup-delay=%s", startupDelay), + }, Ports: []v1.ContainerPort{{ Name: "http", - ContainerPort: 8080, + ContainerPort: healthserverPort, + HostPort: healthserverPort, }}, + // SecurityContext: let OpenShift assign the UID from the + // namespace range. The privileged SCC handles hostNetwork. + SecurityContext: &v1.SecurityContext{ + AllowPrivilegeEscalation: ptrBool(false), + Capabilities: &v1.Capabilities{ + Drop: []v1.Capability{"ALL"}, + }, + SeccompProfile: &v1.SeccompProfile{ + Type: v1.SeccompProfileTypeRuntimeDefault, + }, + }, Env: []v1.EnvVar{{ Name: "POD_NAME", ValueFrom: &v1.EnvVarSource{ @@ -1415,7 +1454,7 @@ func buildHealthTransitionService(namespace, name, deployName string) *v1.Servic "service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true", "service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol": "HTTP", "service.beta.kubernetes.io/aws-load-balancer-healthcheck-path": "/readyz", - "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": "traffic-port", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": fmt.Sprintf("%d", healthserverPort), "service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval": "10", "service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold": "2", "service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold": "2", @@ -1428,8 +1467,8 @@ func buildHealthTransitionService(namespace, name, deployName string) *v1.Servic Ports: []v1.ServicePort{{ Name: "http", Protocol: v1.ProtocolTCP, - Port: 80, - TargetPort: intstr.FromInt(8080), + Port: int32(healthserverPort), + TargetPort: intstr.FromInt(healthserverPort), }}, }, } @@ -1448,3 +1487,33 @@ func waitForLBDeletion(ctx context.Context, lbDNS string) { return lb == nil, nil }) } + +// grantHostNetworkSCC creates a RoleBinding that grants the default service +// account in the given namespace access to the privileged SCC. This is +// required on OpenShift for pods with hostNetwork: true. The privileged SCC +// allows hostNetwork, hostPort, and any UID — matching what static pods +// (like KAS) use on control-plane nodes. +func grantHostNetworkSCC(ctx context.Context, cs clientset.Interface, namespace string) { + rbName := "healthserver-privileged" + rb := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: rbName, + Namespace: namespace, + }, + Subjects: []rbacv1.Subject{{ + Kind: "ServiceAccount", + Name: "default", + Namespace: namespace, + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "system:openshift:scc:privileged", + }, + } + _, err := cs.RbacV1().RoleBindings(namespace).Create(ctx, rb, metav1.CreateOptions{}) + framework.ExpectNoError(err, "grant privileged SCC to default SA") +} + +func ptrBool(b bool) *bool { return &b } +func ptrInt64(i int64) *int64 { return &i } From c962a18ee0f89462545fe3fc15b14b5c7dc844d5 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 13 Aug 2026 02:38:22 -0300 Subject: [PATCH 18/22] e2e: unified binary with in-cluster client, aggregator, and metrics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add cmd/e2e-nlb-health-test/ — single binary with three subcommands: serve — health-controllable server with /metrics, HC counters client — in-cluster HTTP request generator on worker nodes aggregator — metrics collector with scrape + push hybrid model Architecture: - Aggregator pod (worker) receives push events from servers and client, scrapes client /metrics every 1s, receives TG snapshots from the test binary. Logs human-readable summaries alongside raw JSON for aggregation. - Server pods push metrics_update every 5s with service_reqs (GET /) and hc_reqs (GET /readyz) as separate counter groups. Server scraping disabled (control-plane SG blocks inbound from workers). - Client pod runs on worker node with ~1ms RTT to NLB, achieving ~240 req/s with 16 workers at 50ms interval. - All agents register with POD_IP env var (downward API) so the aggregator can reach them by their real IPs. Test flow changes: - Deploy aggregator first, then servers with --aggregator flag, then NLB, then in-cluster client after TG targets healthy. - TG observer pushes snapshots to aggregator every 2s. - Wait for TG unhealthy before waiting for healthy after pod delete. - Fetch client records via K8s API proxy (works for worker pods). - Phase naming: GracefulShutdown (t5→t7) captures the SIGTERM→NLB propagation window. Restart (t7→t9) starts after last routed req. - Timeline labels: "pod deleted (SIGTERM sent)" for clarity. - All timestamps in UTC for consistency. - Cleanup covers all resources (aggregator, client, server, NLB). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../cmd/e2e-nlb-health-test/Dockerfile | 10 + .../cmd/e2e-nlb-health-test/aggregator.go | 396 ++++++++++++++ .../cmd/e2e-nlb-health-test/client.go | 330 ++++++++++++ .../cmd/e2e-nlb-health-test/main.go | 41 ++ .../cmd/e2e-nlb-health-test/serve.go | 380 ++++++++++++++ .../cmd/e2e-nlb-health-test/types.go | 112 ++++ .../e2e/aws/lb_health_transition.go | 489 +++++++++++++++--- 7 files changed, 1690 insertions(+), 68 deletions(-) create mode 100644 openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/Dockerfile create mode 100644 openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/aggregator.go create mode 100644 openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go create mode 100644 openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/main.go create mode 100644 openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go create mode 100644 openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/types.go diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/Dockerfile b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/Dockerfile new file mode 100644 index 000000000..652d63a88 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/Dockerfile @@ -0,0 +1,10 @@ +FROM golang:1.22-alpine AS builder +WORKDIR /build +COPY *.go . +RUN go mod init e2e-nlb-health-test && \ + go mod edit -go=1.22 && \ + CGO_ENABLED=0 go build -ldflags="-s -w" -o e2e-nlb-health-test . + +FROM scratch +COPY --from=builder /build/e2e-nlb-health-test /e2e-nlb-health-test +ENTRYPOINT ["/e2e-nlb-health-test"] diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/aggregator.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/aggregator.go new file mode 100644 index 000000000..0d738c685 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/aggregator.go @@ -0,0 +1,396 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "sort" + "strings" + "sync" + "syscall" + "time" +) + +// AgentInfo describes a registered agent (server or client). +type AgentInfo struct { + Role string `json:"role"` // "server" or "client" + URL string `json:"url"` // base URL (e.g., http://10.0.22.243:19443) + ServerID string `json:"server_id,omitempty"` // only for role="server" +} + +// aggregator holds all mutable state for the aggregator process. +type aggregator struct { + mu sync.Mutex + agents map[string][]AgentInfo // role -> []AgentInfo + events []Event + timeseries []TimeseriesRow + latestTG *TGSnapshot +} + +func runAggregator(args []string) { + fs := flag.NewFlagSet("aggregator", flag.ExitOnError) + port := fs.Int("port", 8090, "port to serve aggregator API") + scrapeInterval := fs.Duration("scrape-interval", 1*time.Second, "how often to scrape agent /metrics endpoints") + if err := fs.Parse(args); err != nil { + log.Fatalf("aggregator: failed to parse flags: %v", err) + } + + agg := &aggregator{ + agents: make(map[string][]AgentInfo), + } + + mux := http.NewServeMux() + + // Receive endpoints + mux.HandleFunc("/register", agg.handleRegister) + mux.HandleFunc("/event", agg.handleEvent) + mux.HandleFunc("/tg-snapshot", agg.handleTGSnapshot) + + // Serve endpoints + mux.HandleFunc("/timeline", agg.handleTimeline) + mux.HandleFunc("/timeseries", agg.handleTimeseries) + mux.HandleFunc("/report", agg.handleReport) + mux.HandleFunc("/healthz", handleHealthz) + + server := &http.Server{ + Addr: fmt.Sprintf(":%d", *port), + Handler: mux, + } + + // Signal handling: on SIGTERM, stop scraping but keep serving for 60s. + stopScrape := make(chan struct{}) + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGTERM) + + go func() { + <-sigCh + log.Println("aggregator: received SIGTERM, stopping scrape loop") + close(stopScrape) + + // Print shutdown summary. + agg.mu.Lock() + log.Printf("aggregator: === SHUTDOWN SUMMARY ===") + log.Printf("aggregator: total events: %d", len(agg.events)) + log.Printf("aggregator: total timeseries rows: %d", len(agg.timeseries)) + // Print the last timeseries row if available + if len(agg.timeseries) > 0 { + last := agg.timeseries[len(agg.timeseries)-1] + for sid, sm := range last.Servers { + log.Printf("aggregator: server %s: state=%s reqs=%d readyz_reqs=%d", sid, sm.State, sm.Counters.TotalRequests, sm.Counters.ReadyzRequests) + } + if last.Client != nil { + log.Printf("aggregator: client: total=%d 2xx=%d errors=%d pre_readyz=%d", last.Client.Counters.TotalSent, last.Client.Counters.Status2xx, last.Client.Counters.Errors, last.Client.Counters.PreReadyz) + } + if last.TG != nil { + log.Printf("aggregator: tg: healthy=%d unhealthy=%d initial=%d", last.TG.HealthyCount, last.TG.UnhealthyCount, last.TG.InitialCount) + } + } + // Print all events + log.Printf("aggregator: === EVENT TIMELINE ===") + for _, e := range agg.events { + log.Printf("aggregator: %s source=%s server_id=%s event=%s", e.Timestamp.UTC().Format("15:04:05"), e.Source, e.ServerID, e.Event) + } + log.Printf("aggregator: === END SUMMARY ===") + agg.mu.Unlock() + + // Keep serving for 60s so the test binary can fetch the final report. + time.Sleep(60 * time.Second) + log.Println("aggregator: grace period elapsed, shutting down HTTP server") + server.Close() + }() + + // Start scrape loop + go agg.scrapeLoop(*scrapeInterval, stopScrape) + + log.Printf("aggregator: listening on :%d, scrape-interval=%s", *port, *scrapeInterval) + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("aggregator: server error: %v", err) + } + log.Println("aggregator: shut down") +} + +// ---------- scrape loop ---------- + +func (a *aggregator) scrapeLoop(interval time.Duration, stop <-chan struct{}) { + client := &http.Client{Timeout: 5 * time.Second} + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-stop: + log.Println("aggregator: scrape loop stopped") + return + case <-ticker.C: + a.scrapeOnce(client) + } + } +} + +func (a *aggregator) scrapeOnce(client *http.Client) { + a.mu.Lock() + servers := make([]AgentInfo, len(a.agents["server"])) + copy(servers, a.agents["server"]) + clients := make([]AgentInfo, len(a.agents["client"])) + copy(clients, a.agents["client"]) + latestTG := a.latestTG + a.mu.Unlock() + + row := TimeseriesRow{ + Timestamp: time.Now().UTC(), + Servers: make(map[string]ServerMetrics), + } + + // Servers push metrics via events (metrics_update) rather than being + // scraped, because the control-plane security group blocks inbound + // traffic from worker nodes on the healthserver port. Server metrics + // arrive through pushEvent and are captured in the events list. + _ = servers // registered but not scraped + + // Scrape client (take the first registered client) + if len(clients) > 0 { + cm, err := scrapeClientMetrics(client, clients[0].URL) + if err != nil { + log.Printf("aggregator: scrape client (%s): %v", clients[0].URL, err) + } else { + row.Client = cm + } + } + + // Attach latest TG snapshot + if latestTG != nil { + row.TG = latestTG + } + + a.mu.Lock() + a.timeseries = append(a.timeseries, row) + a.mu.Unlock() +} + +func scrapeServerMetrics(client *http.Client, baseURL string) (*ServerMetrics, error) { + resp, err := client.Get(baseURL + "/metrics") + if err != nil { + return nil, err + } + defer resp.Body.Close() + var sm ServerMetrics + if err := json.NewDecoder(resp.Body).Decode(&sm); err != nil { + return nil, fmt.Errorf("decode: %w", err) + } + return &sm, nil +} + +func scrapeClientMetrics(client *http.Client, baseURL string) (*ClientMetrics, error) { + resp, err := client.Get(baseURL + "/metrics") + if err != nil { + return nil, err + } + defer resp.Body.Close() + var cm ClientMetrics + if err := json.NewDecoder(resp.Body).Decode(&cm); err != nil { + return nil, fmt.Errorf("decode: %w", err) + } + return &cm, nil +} + +// ---------- receive handlers ---------- + +func (a *aggregator) handleRegister(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + var info AgentInfo + if err := json.NewDecoder(r.Body).Decode(&info); err != nil { + http.Error(w, "bad request: "+err.Error(), http.StatusBadRequest) + return + } + if info.Role == "" || info.URL == "" { + http.Error(w, "bad request: role and url are required", http.StatusBadRequest) + return + } + + a.mu.Lock() + a.agents[info.Role] = append(a.agents[info.Role], info) + a.events = append(a.events, Event{ + Source: info.Role, + ServerID: info.ServerID, + Event: EventRegistered, + Detail: fmt.Sprintf("registered %s at %s", info.Role, info.URL), + Timestamp: time.Now().UTC(), + }) + a.mu.Unlock() + + log.Printf("aggregator: registered %s agent: %s (server_id=%s)", info.Role, info.URL, info.ServerID) + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{"status":"ok"}`) +} + +func (a *aggregator) handleEvent(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + var ev Event + if err := json.NewDecoder(r.Body).Decode(&ev); err != nil { + http.Error(w, "bad request: "+err.Error(), http.StatusBadRequest) + return + } + if ev.Timestamp.IsZero() { + ev.Timestamp = time.Now().UTC() + } + + a.mu.Lock() + a.events = append(a.events, ev) + a.mu.Unlock() + + // Log all events with raw detail (JSON preserved for aggregation). + // For metrics_update, add a human-readable summary line after the raw log. + log.Printf("aggregator: event: source=%s server_id=%s event=%s detail=%s", + ev.Source, ev.ServerID, ev.Event, ev.Detail) + + if ev.Event == "metrics_update" && ev.Source == "server" { + var sm ServerMetrics + if json.Unmarshal([]byte(ev.Detail), &sm) == nil { + log.Printf("aggregator: server %s: state=%s | service_reqs=%d | hc_reqs=%d (hc_200=%d hc_503=%d)", + sm.ServerID, sm.State, sm.Counters.MainRequests, + sm.Counters.ReadyzRequests, sm.Counters.Readyz200, sm.Counters.Readyz503) + } + } else if ev.Event == "metrics_update" && ev.Source == "client" { + var cm ClientMetrics + if json.Unmarshal([]byte(ev.Detail), &cm) == nil { + log.Printf("aggregator: client: sent=%d 2xx=%d errors=%d pre_readyz=%d", + cm.Counters.TotalSent, cm.Counters.Status2xx, cm.Counters.Errors, cm.Counters.PreReadyz) + } + } + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{"status":"ok"}`) +} + +func (a *aggregator) handleTGSnapshot(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + var snap TGSnapshot + if err := json.NewDecoder(r.Body).Decode(&snap); err != nil { + http.Error(w, "bad request: "+err.Error(), http.StatusBadRequest) + return + } + if snap.Timestamp.IsZero() { + snap.Timestamp = time.Now().UTC() + } + + a.mu.Lock() + a.latestTG = &snap + a.mu.Unlock() + + // Single-line TG snapshot with per-target state + var parts []string + for id, state := range snap.Targets { + parts = append(parts, fmt.Sprintf("%s=%s", id, state)) + } + log.Printf("aggregator: tg-snapshot: healthy=%d unhealthy=%d initial=%d | %s", + snap.HealthyCount, snap.UnhealthyCount, snap.InitialCount, strings.Join(parts, ", ")) + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{"status":"ok"}`) +} + +// ---------- serve handlers ---------- + +func (a *aggregator) handleTimeline(w http.ResponseWriter, r *http.Request) { + a.mu.Lock() + events := make([]Event, len(a.events)) + copy(events, a.events) + a.mu.Unlock() + + sort.Slice(events, func(i, j int) bool { + return events[i].Timestamp.Before(events[j].Timestamp) + }) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(events) +} + +func (a *aggregator) handleTimeseries(w http.ResponseWriter, r *http.Request) { + a.mu.Lock() + ts := make([]TimeseriesRow, len(a.timeseries)) + copy(ts, a.timeseries) + a.mu.Unlock() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(ts) +} + +func (a *aggregator) handleReport(w http.ResponseWriter, r *http.Request) { + a.mu.Lock() + events := make([]Event, len(a.events)) + copy(events, a.events) + tsCopy := make([]TimeseriesRow, len(a.timeseries)) + copy(tsCopy, a.timeseries) + latestTG := a.latestTG + a.mu.Unlock() + + sort.Slice(events, func(i, j int) bool { + return events[i].Timestamp.Before(events[j].Timestamp) + }) + + var b strings.Builder + b.WriteString("=== AGGREGATOR REPORT ===\n\n") + + // Events + b.WriteString(fmt.Sprintf("--- Events (%d total) ---\n", len(events))) + for _, ev := range events { + b.WriteString(fmt.Sprintf(" [%s] src=%-8s server_id=%-20s event=%-22s detail=%s\n", + ev.Timestamp.Format(time.RFC3339Nano), + ev.Source, ev.ServerID, ev.Event, ev.Detail)) + } + b.WriteString("\n") + + // Latest metrics from the most recent timeseries row + if len(tsCopy) > 0 { + latest := tsCopy[len(tsCopy)-1] + b.WriteString(fmt.Sprintf("--- Latest Metrics (at %s) ---\n", latest.Timestamp.Format(time.RFC3339Nano))) + + if len(latest.Servers) > 0 { + b.WriteString(" Servers:\n") + for id, sm := range latest.Servers { + b.WriteString(fmt.Sprintf(" [%s] state=%s total_req=%d main_req=%d readyz_req=%d readyz_200=%d readyz_503=%d\n", + id, sm.State, + sm.Counters.TotalRequests, sm.Counters.MainRequests, + sm.Counters.ReadyzRequests, sm.Counters.Readyz200, sm.Counters.Readyz503)) + } + } + + if latest.Client != nil { + c := latest.Client.Counters + b.WriteString(fmt.Sprintf(" Client: sent=%d 2xx=%d 4xx=%d 5xx=%d errors=%d pre_readyz=%d\n", + c.TotalSent, c.Status2xx, c.Status4xx, c.Status5xx, c.Errors, c.PreReadyz)) + } + } + + // Latest TG snapshot + if latestTG != nil { + b.WriteString(fmt.Sprintf("\n--- Latest TG Snapshot (at %s) ---\n", latestTG.Timestamp.Format(time.RFC3339Nano))) + b.WriteString(fmt.Sprintf(" healthy=%d unhealthy=%d initial=%d\n", + latestTG.HealthyCount, latestTG.UnhealthyCount, latestTG.InitialCount)) + for target, state := range latestTG.Targets { + b.WriteString(fmt.Sprintf(" %s -> %s\n", target, state)) + } + } + + b.WriteString(fmt.Sprintf("\nTimeseries rows collected: %d\n", len(tsCopy))) + b.WriteString("=== END REPORT ===\n") + + w.Header().Set("Content-Type", "text/plain") + fmt.Fprint(w, b.String()) +} + +func handleHealthz(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, "ok") +} diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go new file mode 100644 index 000000000..252ad4757 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go @@ -0,0 +1,330 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "log" + "net" + "net/http" + "net/http/httptrace" + "os" + "os/signal" + "sync" + "sync/atomic" + "syscall" + "time" +) + +func runClient(args []string) { + fs := flag.NewFlagSet("client", flag.ExitOnError) + url := fs.String("url", "", "NLB URL to send requests to (required)") + interval := fs.Duration("interval", 100*time.Millisecond, "interval per worker") + workers := fs.Int("workers", 8, "parallel request goroutines") + port := fs.Int("port", 8080, "port to serve metrics/records API") + aggregatorURL := fs.String("aggregator", "", "aggregator URL for pushing events") + fs.Parse(args) + + if *url == "" { + fmt.Fprintf(os.Stderr, "client: --url is required\n") + os.Exit(1) + } + + log.Printf("[client] starting workers=%d interval=%s url=%s", *workers, *interval, *url) + + // Register with aggregator if configured. + if *aggregatorURL != "" { + podIP := os.Getenv("POD_IP") + if podIP == "" { + podIP = "localhost" + } + regBody, _ := json.Marshal(map[string]string{ + "role": "client", + "url": fmt.Sprintf("http://%s:%d", podIP, *port), + "server_id": "healthtest-client", + }) + resp, err := http.Post(*aggregatorURL+"/register", "application/json", bytes.NewReader(regBody)) + if err != nil { + log.Printf("[client] aggregator registration failed: %v", err) + } else { + resp.Body.Close() + log.Printf("[client] registered with aggregator at %s", *aggregatorURL) + } + pushEvent(*aggregatorURL, Event{ + Source: "client", + Event: "client_started", + Timestamp: time.Now(), + }) + } + + // Shared state: atomic counters. + var ( + totalSent int64 + status2xx int64 + status4xx int64 + status5xx int64 + errors int64 + preReadyz int64 + ) + + // Records slice protected by mutex. + var ( + recordsMu sync.Mutex + records []ClientRecord + ) + + // Per-server map protected by its own mutex. + var ( + perServerMu sync.Mutex + perServer = make(map[string]PerServerCount) + ) + + // Stop channel to signal workers to stop sending. + stopCh := make(chan struct{}) + + // HTTP client that creates a new TCP connection for every request. + httpClient := &http.Client{ + Transport: &http.Transport{ + DisableKeepAlives: true, + }, + Timeout: 10 * time.Second, + } + + // sendRequest performs a single GET to the NLB URL with connection tracing. + sendRequest := func() { + var ( + targetIP string + dialStart time.Time + dialDur time.Duration + ) + + trace := &httptrace.ClientTrace{ + ConnectStart: func(network, addr string) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + targetIP = addr + } else { + targetIP = host + } + dialStart = time.Now() + }, + ConnectDone: func(network, addr string, err error) { + dialDur = time.Since(dialStart) + }, + } + + req, err := http.NewRequest("GET", *url, nil) + if err != nil { + log.Printf("[client] failed to create request: %v", err) + return + } + req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) + + atomic.AddInt64(&totalSent, 1) + + now := time.Now() + resp, err := httpClient.Do(req) + + rec := ClientRecord{ + Timestamp: now, + TargetIP: targetIP, + TCPDialDuration: dialDur, + } + + if err != nil { + atomic.AddInt64(&errors, 1) + rec.Error = err.Error() + + recordsMu.Lock() + records = append(records, rec) + recordsMu.Unlock() + return + } + defer resp.Body.Close() + + rec.HTTPStatus = resp.StatusCode + rec.ServerState = resp.Header.Get("X-Server-State") + rec.ServerID = resp.Header.Get("X-Server-ID") + rec.FirstReadyzTime = resp.Header.Get("X-First-Readyz-Time") + + // Classify status code. + switch { + case resp.StatusCode >= 200 && resp.StatusCode < 300: + atomic.AddInt64(&status2xx, 1) + case resp.StatusCode >= 400 && resp.StatusCode < 500: + atomic.AddInt64(&status4xx, 1) + case resp.StatusCode >= 500 && resp.StatusCode < 600: + atomic.AddInt64(&status5xx, 1) + } + + // Detect pre-readyz response. + if rec.ServerState == "pre-readyz" { + rec.IsNonReadyReq = true + atomic.AddInt64(&preReadyz, 1) + + pushEvent(*aggregatorURL, Event{ + Source: "client", + Event: EventPreReadyz, + ServerID: rec.ServerID, + Detail: fmt.Sprintf("target_ip=%s", rec.TargetIP), + Timestamp: time.Now(), + }) + } + + // Update per-server map. + if rec.ServerID != "" { + perServerMu.Lock() + ps := perServer[rec.ServerID] + ps.Total++ + if rec.IsNonReadyReq { + ps.PreReadyz++ + } + perServer[rec.ServerID] = ps + perServerMu.Unlock() + } + + recordsMu.Lock() + records = append(records, rec) + recordsMu.Unlock() + } + + // Start worker goroutines. + var wg sync.WaitGroup + for i := 0; i < *workers; i++ { + wg.Add(1) + go func(workerID int) { + defer wg.Done() + ticker := time.NewTicker(*interval) + defer ticker.Stop() + for { + select { + case <-stopCh: + return + case <-ticker.C: + sendRequest() + } + } + }(i) + } + + // Serve metrics, records, and healthz endpoints. + mux := http.NewServeMux() + + mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { + perServerMu.Lock() + psCopy := make(map[string]PerServerCount, len(perServer)) + for k, v := range perServer { + psCopy[k] = v + } + perServerMu.Unlock() + + m := ClientMetrics{ + Timestamp: time.Now(), + Counters: ClientCounters{ + TotalSent: atomic.LoadInt64(&totalSent), + Status2xx: atomic.LoadInt64(&status2xx), + Status4xx: atomic.LoadInt64(&status4xx), + Status5xx: atomic.LoadInt64(&status5xx), + Errors: atomic.LoadInt64(&errors), + PreReadyz: atomic.LoadInt64(&preReadyz), + }, + PerServer: psCopy, + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(m) + }) + + mux.HandleFunc("/records", func(w http.ResponseWriter, r *http.Request) { + recordsMu.Lock() + recsCopy := make([]ClientRecord, len(records)) + copy(recsCopy, records) + recordsMu.Unlock() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(recsCopy) + }) + + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "ok\n") + }) + + addr := fmt.Sprintf(":%d", *port) + server := &http.Server{ + Addr: addr, + Handler: mux, + } + + go func() { + log.Printf("[client] serving metrics/records on %s", addr) + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("[client] ListenAndServe failed: %v", err) + } + }() + + // Log stats periodically. + go func() { + for { + time.Sleep(10 * time.Second) + log.Printf("[client] sent=%d 2xx=%d 4xx=%d 5xx=%d err=%d pre_readyz=%d", + atomic.LoadInt64(&totalSent), + atomic.LoadInt64(&status2xx), + atomic.LoadInt64(&status4xx), + atomic.LoadInt64(&status5xx), + atomic.LoadInt64(&errors), + atomic.LoadInt64(&preReadyz), + ) + } + }() + + // Push metrics to aggregator periodically so it has client data + // even if scraping fails. + if *aggregatorURL != "" { + go func() { + for { + time.Sleep(5 * time.Second) + + perServerMu.Lock() + psCopy := make(map[string]PerServerCount, len(perServer)) + for k, v := range perServer { + psCopy[k] = v + } + perServerMu.Unlock() + + m := ClientMetrics{ + Timestamp: time.Now(), + Counters: ClientCounters{ + TotalSent: atomic.LoadInt64(&totalSent), + Status2xx: atomic.LoadInt64(&status2xx), + Status4xx: atomic.LoadInt64(&status4xx), + Status5xx: atomic.LoadInt64(&status5xx), + Errors: atomic.LoadInt64(&errors), + PreReadyz: atomic.LoadInt64(&preReadyz), + }, + PerServer: psCopy, + } + metricsJSON, _ := json.Marshal(m) + pushEvent(*aggregatorURL, Event{ + Source: "client", + Event: "metrics_update", + Detail: string(metricsJSON), + Timestamp: time.Now(), + }) + } + }() + } + + // Handle SIGTERM: stop sending, keep serving for 60s, then exit. + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGTERM) + <-sigCh + + log.Printf("[client] received SIGTERM, stopping workers") + close(stopCh) + wg.Wait() + log.Printf("[client] all workers stopped, keeping metrics server alive for 60s") + + time.Sleep(60 * time.Second) + log.Printf("[client] grace period elapsed, exiting") +} diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/main.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/main.go new file mode 100644 index 000000000..ea0f5c565 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + if len(os.Args) < 2 { + printUsage() + os.Exit(1) + } + + switch os.Args[1] { + case "serve": + runServe(os.Args[2:]) + case "client": + runClient(os.Args[2:]) + case "aggregator": + runAggregator(os.Args[2:]) + default: + fmt.Fprintf(os.Stderr, "unknown subcommand: %s\n", os.Args[1]) + printUsage() + os.Exit(1) + } +} + +func printUsage() { + fmt.Fprintf(os.Stderr, `Usage: e2e-nlb-health-test [flags] + +Subcommands: + serve Health-controllable HTTP server (runs on control-plane nodes) + client HTTP request generator (runs on worker nodes) + aggregator Metrics aggregator and report generator (runs on worker node) + +Examples: + e2e-nlb-health-test serve --port=19443 --startup-delay=60s --aggregator=http://agg:8090 + e2e-nlb-health-test client --url=http://NLB:19443/ --workers=8 --aggregator=http://agg:8090 + e2e-nlb-health-test aggregator --port=8090 --scrape-interval=1s +`) +} diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go new file mode 100644 index 000000000..911f43a01 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go @@ -0,0 +1,380 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "sync" + "sync/atomic" + "syscall" + "time" +) + +func runServe(args []string) { + fs := flag.NewFlagSet("serve", flag.ExitOnError) + port := fs.Int("port", 19443, "service port") + startupDelay := fs.Duration("startup-delay", 30*time.Second, "time before /readyz returns 200") + aggregatorURL := fs.String("aggregator", "", "aggregator URL for pushing events") + fs.Parse(args) + + // Server identity: POD_NAME env var, fallback to hostname. + serverID := os.Getenv("POD_NAME") + if serverID == "" { + h, err := os.Hostname() + if err != nil { + serverID = "unknown" + } else { + serverID = h + } + } + + processStart := time.Now() + + // Shared mutable state protected by mutex / atomics. + var ( + mu sync.Mutex + state = "pre-readyz" + readyzReady = false + lifecycle = Lifecycle{ProcessStart: &processStart} + totalRequests int64 + mainRequests int64 + readyzRequests int64 + readyz200 int64 + readyz503 int64 + ) + + getState := func() string { + mu.Lock() + defer mu.Unlock() + return state + } + + // GET / — main endpoint. Client requests go here via NLB. + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + count := atomic.AddInt64(&mainRequests, 1) + atomic.AddInt64(&totalRequests, 1) + + // Log first request to confirm traffic is reaching the server + if count == 1 { + log.Printf("[serve] first main request from %s", r.RemoteAddr) + } + + mu.Lock() + s := state + frt := lifecycle.FirstReadyz200 + mu.Unlock() + + w.Header().Set("X-Server-State", s) + w.Header().Set("X-Server-ID", serverID) + w.Header().Set("X-Server-Start-Time", processStart.Format(time.RFC3339Nano)) + if frt != nil { + w.Header().Set("X-First-Readyz-Time", frt.Format(time.RFC3339Nano)) + } + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "ok\n") + }) + + // GET /readyz — health check endpoint. NLB HC probes and any direct + // /readyz requests are counted here. If readyz_requests stays 0, the + // NLB HC is going through kube-proxy's HC NodePort, not our port. + http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) { + count := atomic.AddInt64(&readyzRequests, 1) + atomic.AddInt64(&totalRequests, 1) + + mu.Lock() + ready := readyzReady + mu.Unlock() + + // Log first few HC probes and then every 100th to confirm they arrive + if count <= 3 || count%100 == 0 { + log.Printf("[serve] /readyz probe #%d from %s ready=%v", count, r.RemoteAddr, ready) + } + + if ready { + atomic.AddInt64(&readyz200, 1) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "ok\n") + } else { + atomic.AddInt64(&readyz503, 1) + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintf(w, "not ready\n") + } + }) + + // POST /admin/readyz?ready=true|false — control readyz state + http.HandleFunc("/admin/readyz", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + val := r.URL.Query().Get("ready") + mu.Lock() + switch val { + case "true": + readyzReady = true + if state == "pre-readyz" || state == "draining" { + state = "ready" + } + now := time.Now() + if lifecycle.FirstReadyz200 == nil { + lifecycle.FirstReadyz200 = &now + } + mu.Unlock() + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: EventReadyzTrue, + Timestamp: now, + }) + case "false": + readyzReady = false + now := time.Now() + lifecycle.ReadyzFalseAt = &now + if state == "ready" { + state = "draining" + } + mu.Unlock() + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: EventReadyzFalse, + Timestamp: now, + }) + default: + mu.Unlock() + http.Error(w, "ready param must be true or false", http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "readyz=%s\n", val) + }) + + // POST /admin/shutdown?delay=Ns — graceful shutdown + http.HandleFunc("/admin/shutdown", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + delayStr := r.URL.Query().Get("delay") + delay := time.Duration(0) + if delayStr != "" { + d, err := time.ParseDuration(delayStr) + if err != nil { + http.Error(w, fmt.Sprintf("invalid delay: %v", err), http.StatusBadRequest) + return + } + delay = d + } + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "shutting down in %s\n", delay) + + go func() { + if delay > 0 { + time.Sleep(delay) + } + mu.Lock() + state = "shutdown" + mu.Unlock() + log.Printf("[serve] shutdown requested, exiting") + os.Exit(0) + }() + }) + + // GET /admin/lifecycle — JSON lifecycle timestamps + http.HandleFunc("/admin/lifecycle", func(w http.ResponseWriter, r *http.Request) { + mu.Lock() + lc := lifecycle + mu.Unlock() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(lc) + }) + + // GET /metrics — returns ServerMetrics JSON + http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { + mu.Lock() + lc := lifecycle + s := state + mu.Unlock() + + m := ServerMetrics{ + ServerID: serverID, + State: s, + Timestamp: time.Now(), + Lifecycle: lc, + Counters: ServerCounters{ + TotalRequests: atomic.LoadInt64(&totalRequests), + MainRequests: atomic.LoadInt64(&mainRequests), + ReadyzRequests: atomic.LoadInt64(&readyzRequests), + Readyz200: atomic.LoadInt64(&readyz200), + Readyz503: atomic.LoadInt64(&readyz503), + }, + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(m) + }) + + // Start TCP listener. + addr := fmt.Sprintf(":%d", *port) + log.Printf("[serve] starting server id=%s on %s (startup-delay=%s)", serverID, addr, *startupDelay) + + // Record t_tcp_up and push event. + now := time.Now() + mu.Lock() + lifecycle.TCPUp = &now + mu.Unlock() + + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: EventTCPUp, + Timestamp: now, + }) + + // Register with aggregator if configured. + if *aggregatorURL != "" { + podIP := os.Getenv("POD_IP") + if podIP == "" { + podIP = "localhost" + } + regURL := fmt.Sprintf("http://%s:%d", podIP, *port) + regBody, _ := json.Marshal(map[string]string{"role": "server", "url": regURL, "server_id": serverID}) + resp, err := http.Post(*aggregatorURL+"/register", "application/json", bytes.NewReader(regBody)) + if err != nil { + log.Printf("[serve] aggregator registration failed: %v", err) + } else { + resp.Body.Close() + log.Printf("[serve] registered with aggregator at %s (url=%s)", *aggregatorURL, regURL) + } + } + + // Push server metrics to aggregator every 5 seconds so the aggregator + // has server-side data even though it can't scrape servers directly + // (control-plane SG blocks inbound from worker nodes). + if *aggregatorURL != "" { + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + for range ticker.C { + mu.Lock() + metrics := ServerMetrics{ + ServerID: serverID, + State: state, + Timestamp: time.Now().UTC(), + Lifecycle: lifecycle, + Counters: ServerCounters{ + TotalRequests: atomic.LoadInt64(&totalRequests), + MainRequests: atomic.LoadInt64(&mainRequests), + ReadyzRequests: atomic.LoadInt64(&readyzRequests), + Readyz200: atomic.LoadInt64(&readyz200), + Readyz503: atomic.LoadInt64(&readyz503), + }, + } + mu.Unlock() + detail, _ := json.Marshal(metrics) + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: "metrics_update", + Detail: string(detail), + Timestamp: metrics.Timestamp, + }) + } + }() + } + + // Schedule readyz transition after startup-delay. + go func() { + time.Sleep(*startupDelay) + + mu.Lock() + // Only transition if we haven't been sigterm'd or manually set. + if state == "pre-readyz" { + readyzReady = true + state = "ready" + t := time.Now() + lifecycle.FirstReadyz200 = &t + mu.Unlock() + + log.Printf("[serve] startup-delay elapsed, readyz=true") + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: EventReadyzTrue, + Timestamp: t, + }) + } else { + mu.Unlock() + } + }() + + // Handle SIGTERM: set draining, readyz→503, but keep serving. + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGTERM) + go func() { + <-sigCh + now := time.Now() + log.Printf("[serve] received SIGTERM, state→draining, readyz→503") + + mu.Lock() + state = "draining" + readyzReady = false + lifecycle.Sigterm = &now + lifecycle.ReadyzFalseAt = &now + mu.Unlock() + + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: EventSigterm, + Timestamp: now, + }) + pushEvent(*aggregatorURL, Event{ + Source: "server", + ServerID: serverID, + Event: EventReadyzFalse, + Timestamp: now, + }) + // Do NOT call srv.Shutdown() — keep serving until kubelet kills + // at terminationGracePeriodSeconds. + }() + + // Log state periodically. + go func() { + for { + time.Sleep(10 * time.Second) + log.Printf("[serve] state=%s total=%d main=%d readyz=%d (200=%d 503=%d)", + getState(), + atomic.LoadInt64(&totalRequests), + atomic.LoadInt64(&mainRequests), + atomic.LoadInt64(&readyzRequests), + atomic.LoadInt64(&readyz200), + atomic.LoadInt64(&readyz503), + ) + } + }() + + if err := http.ListenAndServe(addr, nil); err != nil { + log.Fatalf("[serve] ListenAndServe failed: %v", err) + } +} + +// pushEvent sends a lifecycle event to the aggregator in a fire-and-forget goroutine. +func pushEvent(aggregatorURL string, evt Event) { + if aggregatorURL == "" { + return + } + go func() { + data, _ := json.Marshal(evt) + http.Post(aggregatorURL+"/event", "application/json", bytes.NewReader(data)) + }() +} diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/types.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/types.go new file mode 100644 index 000000000..5852ad4cc --- /dev/null +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/types.go @@ -0,0 +1,112 @@ +package main + +import "time" + +// Event is a lifecycle event pushed by agents to the aggregator. +// Events capture exact timestamps for one-time state changes +// (SIGTERM, readyz transitions, pre-readyz detection) that may +// fall between scrape intervals. +type Event struct { + Source string `json:"source"` // "server", "client", "tg" + ServerID string `json:"server_id,omitempty"` // pod name or instance ID + Event string `json:"event"` // see event constants below + Detail string `json:"detail,omitempty"` // extra context + Timestamp time.Time `json:"timestamp"` +} + +// Event name constants +const ( + EventSigterm = "sigterm" + EventReadyzFalse = "readyz_false" + EventReadyzTrue = "readyz_true" + EventTCPUp = "tcp_up" + EventPreReadyz = "pre_readyz_detected" + EventRegistered = "agent_registered" +) + +// ServerMetrics is returned by GET /metrics on the serve subcommand. +// Scraped by the aggregator every N seconds. +type ServerMetrics struct { + ServerID string `json:"server_id"` + Node string `json:"node,omitempty"` + State string `json:"state"` + Timestamp time.Time `json:"timestamp"` + Lifecycle Lifecycle `json:"lifecycle"` + Counters ServerCounters `json:"counters"` +} + +// Lifecycle holds server lifecycle timestamps. +type Lifecycle struct { + ProcessStart *time.Time `json:"t_process_start,omitempty"` + TCPUp *time.Time `json:"t_tcp_up,omitempty"` + FirstReadyz200 *time.Time `json:"t_first_readyz_200,omitempty"` + ReadyzFalseAt *time.Time `json:"t_readyz_false_at,omitempty"` + Sigterm *time.Time `json:"t_sigterm,omitempty"` +} + +// ServerCounters tracks request counts by endpoint. readyz_requests +// counts NLB HC probes (only the HC agent calls /readyz). +// main_requests counts client traffic (/). +type ServerCounters struct { + TotalRequests int64 `json:"total_requests"` + MainRequests int64 `json:"main_requests"` + ReadyzRequests int64 `json:"readyz_requests"` + Readyz200 int64 `json:"readyz_200"` + Readyz503 int64 `json:"readyz_503"` +} + +// ClientMetrics is returned by GET /metrics on the client subcommand. +// Scraped by the aggregator every N seconds. +type ClientMetrics struct { + Timestamp time.Time `json:"timestamp"` + Counters ClientCounters `json:"counters"` + PerServer map[string]PerServerCount `json:"per_server"` +} + +// ClientCounters tracks overall client send statistics. +type ClientCounters struct { + TotalSent int64 `json:"total_sent"` + Status2xx int64 `json:"status_2xx"` + Status4xx int64 `json:"status_4xx"` + Status5xx int64 `json:"status_5xx"` + Errors int64 `json:"errors"` + PreReadyz int64 `json:"pre_readyz"` +} + +// PerServerCount tracks how many requests went to a specific server. +type PerServerCount struct { + Total int64 `json:"total"` + PreReadyz int64 `json:"pre_readyz"` +} + +// ClientRecord captures a single HTTP request (sent by client, stored for +// full-detail retrieval via GET /records). +type ClientRecord struct { + Timestamp time.Time `json:"timestamp"` + TargetIP string `json:"target_ip"` + TCPDialDuration time.Duration `json:"tcp_dial_ms"` + HTTPStatus int `json:"http_status"` + ServerState string `json:"server_state"` + ServerID string `json:"server_id"` + FirstReadyzTime string `json:"first_readyz_time"` + IsNonReadyReq bool `json:"is_non_ready_req"` + Error string `json:"error,omitempty"` +} + +// TGSnapshot is a target group health snapshot pushed by the test binary. +type TGSnapshot struct { + Timestamp time.Time `json:"timestamp"` + Targets map[string]string `json:"targets"` + HealthyCount int `json:"healthy_count"` + UnhealthyCount int `json:"unhealthy_count"` + InitialCount int `json:"initial_count"` +} + +// TimeseriesRow is one row of the consolidated time-series, combining +// all three data sources at a single point in time. +type TimeseriesRow struct { + Timestamp time.Time `json:"timestamp"` + Servers map[string]ServerMetrics `json:"servers,omitempty"` + Client *ClientMetrics `json:"client,omitempty"` + TG *TGSnapshot `json:"tg,omitempty"` +} diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index b54976120..f6bdcbbbc 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -2,6 +2,7 @@ package aws import ( "context" + "encoding/json" "fmt" "os" "sort" @@ -26,6 +27,8 @@ import ( ) const ( + // envHealthserverImage is the container image for the unified binary + // e2e-nlb-health-test. Used for all three roles (serve, client, aggregator). envHealthserverImage = "HEALTHSERVER_IMAGE" healthTransitionTestPrefix = e2eTestPrefixLoadBalancer + " health-transition" @@ -35,6 +38,12 @@ const ( // control-plane nodes (verified via netstat). Echoes 6443 (KAS port). healthserverPort = 19443 + // aggregatorPort is the port the aggregator listens on (worker node). + aggregatorPort = 8090 + + // clientPort is the port the in-cluster client serves metrics/records on. + clientPort = 8080 + // kasShutdownDelay matches the KAS shutdown-delay-duration (135s graceful + // margin), simulating how long KAS keeps serving after /readyz→503 before // the process exits. CKAO sets 135s; we add buffer for HC propagation. @@ -45,16 +54,13 @@ const ( // With DisableKeepAlives (new TCP per request), each worker creates // one outbound connection at a time. Too many workers with short // intervals can exhaust ephemeral ports and starve K8s API calls. - // 8 workers at 100ms: best throughput/error ratio from testing. - // Tested configurations (South America → us-east-1, ~430ms RTT): - // 4×200ms = 9.3 req/s, 0.5% errors (baseline) - // 8×100ms = 12.8 req/s, 2.1% errors (sweet spot) - // 16×50ms = 13.6 req/s, 5.3% errors (diminishing returns) - // 40×50ms = port exhaustion / API timeout (broken) - // RTT is the bottleneck, not worker count. More workers from same - // machine just create more connections on the same network path. - defaultClientInterval = 100 * time.Millisecond - defaultClientWorkers = 8 + // With the in-cluster client (~1-5ms RTT to NLB), higher concurrency + // is safe. 16 workers at 50ms = ~320 req/s at 1ms RTT, ~160 req/s + // at 5ms RTT. Port exhaustion is not a concern because the client + // runs inside the cluster on a worker node, not from an external + // machine competing with K8s API calls. + defaultClientInterval = 50 * time.Millisecond + defaultClientWorkers = 16 // postHealthyObserve is how long we continue observing after all targets // become healthy (both initial setup and post-restart). 90s gives enough @@ -148,31 +154,35 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { svcName := "healthserver-lb" // Setup creates NLB targeting master nodes, waits for ALL targets healthy - lbDNS, observer, svcCfg, setupTimes := setupHealthTransition( + _, observer, svcCfg, setupTimes, clientPodName := setupHealthTransition( ctx, cs, ns, deployName, svcName, image, replicas, startupDelay, ) + // The in-cluster client is already running (deployed in setup). + // Start the TG observer for health state tracking, and push + // TG snapshots to the aggregator every 2s so all state changes + // from the AWS perspective appear in the aggregator timeline. observer.Start(ctx) - framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s:%d/", lbDNS, healthserverPort), defaultClientInterval, defaultClientWorkers) - client.Start(ctx) - framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) - defer func() { client.Stop(); observer.Stop() }() - - // Steady state: 90s after all targets healthy — confirms stable - // routing to all replicas before triggering the test scenario. + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + framework.Logf("[observer] started TG health polling (1s) + aggregator push (2s)") + framework.Logf("[client-pod] in-cluster client %s already sending requests", clientPodName) + defer func() { stopTGPush(); observer.Stop() }() + + // Steady state: 90s for the in-cluster client to establish + // traffic to all replicas before triggering the test scenario. By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) time.Sleep(postHealthyObserve) - steadyRecords := client.Records() + // Fetch steady-state records from the in-cluster client + steadyRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) steadyNonReady := 0 for _, r := range steadyRecords { if r.IsNonReadyReq { steadyNonReady++ } } - framework.Logf("[steady] %d requests, %d non-ready", len(steadyRecords), steadyNonReady) + framework.Logf("[steady] %d requests from in-cluster client, %d non-ready", len(steadyRecords), steadyNonReady) Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") By("listing pods to identify target for rollout simulation") @@ -214,8 +224,17 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { podNodeMap[newPod] = newPodObj.Spec.NodeName } - // Wait for the restarted target to become healthy again, then observe - // for postHealthyObserve to confirm stable routing. + // First wait for the TG to detect the unhealthy target (HC + // needs threshold×interval to detect). Without this, the next + // waitForAllTGTargetsHealthy returns immediately because the TG + // hasn't processed the failure yet. + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + // Now wait for the restarted target to recover and become healthy. + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + By("waiting for restarted target to become healthy") err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) framework.ExpectNoError(err, "restarted target healthy") @@ -223,7 +242,8 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) time.Sleep(postHealthyObserve) - allRecords := client.Records() + // Fetch all request records from the in-cluster client pod + allRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) allEvents := observer.Events() tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) @@ -273,13 +293,12 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { deployName := "healthserver" svcName := "healthserver-lb" - lbDNS, observer, svcCfg, setupTimes := setupHealthTransition( + _, observer, svcCfg, setupTimes, clientPodName := setupHealthTransition( ctx, cs, ns, deployName, svcName, image, replicas, startupDelay, ) - // Apply CAPA fix TG attributes BEFORE collecting TG config for report - // and BEFORE starting the observer/client. + // Apply CAPA fix TG attributes BEFORE starting the observer. capaAttrs := map[string]string{ "target_health_state.unhealthy.connection_termination.enabled": "false", "target_health_state.unhealthy.draining_interval_seconds": "300", @@ -296,16 +315,15 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { fetchTGHealthCheckConfig(ctx, &svcCfg) observer.Start(ctx) - framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s:%d/", lbDNS, healthserverPort), defaultClientInterval, defaultClientWorkers) - client.Start(ctx) - framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) - defer func() { client.Stop(); observer.Stop() }() + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + framework.Logf("[observer] started TG health polling (1s) + aggregator push (2s)") + framework.Logf("[client-pod] in-cluster client %s already sending requests", clientPodName) + defer func() { stopTGPush(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) time.Sleep(postHealthyObserve) - steadyRecords := client.Records() + steadyRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) steadyNonReady := 0 for _, r := range steadyRecords { if r.IsNonReadyReq { @@ -347,6 +365,9 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { podNodeMap[newPod] = newPodObj.Spec.NodeName } + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + By("waiting for restarted target to become healthy") err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) framework.ExpectNoError(err, "restarted target healthy") @@ -354,7 +375,8 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) time.Sleep(postHealthyObserve) - allRecords := client.Records() + // Fetch all request records from the in-cluster client pod + allRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) allEvents := observer.Events() tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) @@ -408,17 +430,16 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { deployName := "healthserver" svcName := "healthserver-lb" - lbDNS, observer, svcCfg, setupTimes := setupHealthTransition( + _, observer, svcCfg, setupTimes, clientPodName := setupHealthTransition( ctx, cs, ns, deployName, svcName, image, replicas, startupDelay, ) observer.Start(ctx) - framework.Logf("[observer] started TG health polling (1s interval)") - client := health.NewClient(fmt.Sprintf("http://%s:%d/", lbDNS, healthserverPort), defaultClientInterval, defaultClientWorkers) - client.Start(ctx) - framework.Logf("[client] started %d workers sending requests to %s every %s", defaultClientWorkers, lbDNS, defaultClientInterval) - defer func() { client.Stop(); observer.Stop() }() + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + framework.Logf("[observer] started TG health polling (1s) + aggregator push (2s)") + framework.Logf("[client-pod] in-cluster client %s already sending requests", clientPodName) + defer func() { stopTGPush(); observer.Stop() }() By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) time.Sleep(postHealthyObserve) @@ -451,7 +472,8 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { By(fmt.Sprintf("observing recovery for %s", recoveryObserveDuration)) time.Sleep(recoveryObserveDuration) - allRecords := client.Records() + // Fetch all records from the in-cluster client + allRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) allEvents := observer.Events() tl := computeTimeline52(targetPod, t5, t8, allRecords, allEvents) @@ -460,7 +482,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { tl.T2 = setupTimes.T2 tl.T3 = setupTimes.T3 // t4: first successful client request - for _, r := range client.Records() { + for _, r := range allRecords { if r.Error == "" && r.HTTPStatus > 0 { tl.T4 = r.Timestamp break @@ -495,17 +517,23 @@ func setupHealthTransition( deployName, svcName, image string, replicas int32, startupDelay time.Duration, -) (lbDNS string, observer *health.Observer, cfg serviceConfig, setupTimes transitionTimeline) { +) (lbDNS string, observer *health.Observer, cfg serviceConfig, setupTimes transitionTimeline, clientPodName string) { + + // Deploy the aggregator first — servers and client will connect to it. + // The aggregator runs on a worker node with normal networking. + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + framework.Logf("[aggregator] ready at %s", aggregatorURL) // Grant the default SA in this namespace permission to use hostNetwork // via the OpenShift hostnetwork-v2 SCC. Required because the healthserver // pod uses hostNetwork: true to match KAS static pod behavior. - By("granting hostnetwork-v2 SCC to default service account") + By("granting privileged SCC to default service account") grantHostNetworkSCC(ctx, cs, ns.Name) // t0: deployment created — pods begin scheduling on master nodes By("creating healthserver Deployment (scheduled on master nodes, hostNetwork)") - deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image) + deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image, aggregatorURL) setupTimes.T0 = time.Now() _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) framework.ExpectNoError(err, "create deployment") @@ -531,8 +559,14 @@ func setupHealthTransition( DeferCleanup(func(cleanupCtx context.Context) { framework.Logf("cleaning up health transition resources") - _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + // Clean up all pods/deployments/services created by the test. + // Order: delete NLB service first (triggers LB deletion), then + // pods, then wait for LB to be fully removed from AWS. _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, svcName, metav1.DeleteOptions{}) + _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-client", metav1.DeleteOptions{}) if lbDNS != "" { waitForLBDeletion(cleanupCtx, lbDNS) } @@ -599,7 +633,13 @@ func setupHealthTransition( // t3: all TG targets healthy — HC passed and propagated through Hyperplane setupTimes.T3 = time.Now() - return lbDNS, observer, cfg, setupTimes + // Deploy in-cluster client on a worker node. The client sends requests + // to the NLB with ~1ms RTT (vs ~430ms from external), achieving much + // higher throughput for better detection coverage. + By("deploying in-cluster client on worker node") + clientPodName = deployInClusterClient(ctx, cs, ns.Name, image, lbDNS, aggregatorURL) + + return lbDNS, observer, cfg, setupTimes, clientPodName } // waitForAllTGTargetsHealthy polls DescribeTargetHealth directly (via @@ -638,6 +678,49 @@ func waitForAllTGTargetsHealthy(ctx context.Context, observer *health.Observer, }) } +// waitForTGUnhealthy blocks until at least one TG target reports unhealthy. +// This ensures the NLB HC has detected the failure before we start waiting +// for recovery. Without this, waitForAllTGTargetsHealthy may return +// immediately if called before the HC threshold is met. +func waitForTGUnhealthy(ctx context.Context, observer *health.Observer, timeout time.Duration) { + _ = wait.PollUntilContextTimeout(ctx, 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) { + snap, err := observer.PollOnce(ctx) + if err != nil { + return false, nil + } + if snap.UnhealthyCount > 0 { + framework.Logf("[tg-wait] detected %d unhealthy target(s)", snap.UnhealthyCount) + return true, nil + } + return false, nil + }) +} + +// startTGSnapshotPusher starts a goroutine that pushes TG health snapshots +// to the aggregator every 2 seconds. This runs the observer's PollOnce and +// sends the result to the aggregator so all TG state changes are captured +// in the aggregator's timeline. Returns a cancel function to stop the goroutine. +func startTGSnapshotPusher(ctx context.Context, cs clientset.Interface, namespace string, observer *health.Observer) context.CancelFunc { + ctx, cancel := context.WithCancel(ctx) + go func() { + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + snap, err := observer.PollOnce(ctx) + if err != nil { + continue + } + pushTGSnapshotToAggregator(ctx, cs, namespace, snap) + } + } + }() + return cancel +} + // fetchTGHealthCheckConfig reads the TG's health check settings from the AWS API // and appends them to the serviceConfig for report output. func fetchTGHealthCheckConfig(ctx context.Context, cfg *serviceConfig) { @@ -792,7 +875,7 @@ func computeTimeline( // time to match the test's clock for consistent delta calculations. if tl.T8.IsZero() && r.ServerState == "ready" && r.FirstReadyzTime != "never" && r.FirstReadyzTime != "" { if parsed, err := time.Parse(time.RFC3339Nano, r.FirstReadyzTime); err == nil { - tl.T8 = parsed.Local() + tl.T8 = parsed.UTC() } } @@ -887,11 +970,14 @@ func computeTimeline52( // ─── Report (single block, no per-line logger timestamps) ─────────────────── +// fmtT formats a time in UTC to avoid timezone mismatches between the +// test binary (local TZ) and containers (UTC). All timestamps in the +// report use UTC for consistent comparison. func fmtT(t time.Time) string { if t.IsZero() { return "N/A" } - return t.Format(time.RFC3339) + return t.UTC().Format(time.RFC3339) } func fmtDelta(base, t time.Time) string { @@ -1041,11 +1127,11 @@ func buildReport( // ── Per-phase request breakdown ── // Phases are defined by the timeline milestones: - // Warmup: t3→t5 (all targets healthy, steady-state traffic) - // Shutdown: t5→t7.1 or t5→t8 (readyz→503, target still serving) - // Restart: t7.1→t9 (pod deleted → new target healthy) - // Recovery: t9→end (new target healthy, traffic flowing) - // For Scenario 5.2 (no restart): Shutdown=t5→t8, Recovery=t8→end + // Warmup: t3→t5 (all targets healthy, steady-state traffic) + // GracefulShutdown: t5→t7 (SIGTERM received, readyz→503, NLB still routing) + // Restart: t7→t9 (NLB stopped routing, pod terminated, new pod starting) + // Recovery: t9→end (new target healthy, traffic flowing) + // For Scenario 5.2 (no restart): GracefulShutdown=t5→t8, Recovery=t8→end type phaseStats struct { name string from, to time.Time @@ -1077,12 +1163,12 @@ func buildReport( if !tl.T71.IsZero() { // Scenario 5.5: has restart phase - phases = append(phases, classifyPhase("Shutdown (t5→t7.1)", tl.T5, tl.T71)) - phases = append(phases, classifyPhase("Restart (t7.1→t9)", tl.T71, tl.T9)) + phases = append(phases, classifyPhase("GracefulShutdown (t5→t7)", tl.T5, tl.T7)) + phases = append(phases, classifyPhase("Restart (t7→t9)", tl.T7, tl.T9)) phases = append(phases, classifyPhase("Recovery (t9→end)", tl.T9, time.Time{})) } else { // Scenario 5.2: no restart - phases = append(phases, classifyPhase("Shutdown (t5→t8)", tl.T5, tl.T8)) + phases = append(phases, classifyPhase("GracefulShutdown (t5→t8)", tl.T5, tl.T8)) phases = append(phases, classifyPhase("Recovery (t8→end)", tl.T8, time.Time{})) } @@ -1220,7 +1306,7 @@ func buildReport( addEntry(tl.T5, "t5 readyz→503", "") addEntry(tl.T6, "t6 TG unhealthy", fmtDelta(tl.T5, tl.T6)) addEntry(tl.T7, "t7 last routed req", fmtDelta(tl.T5, tl.T7)) - addEntry(tl.T71, "t7.1 pod deleted", fmtDelta(tl.T5, tl.T71)) + addEntry(tl.T71, "t7.1 pod deleted (SIGTERM sent)", fmtDelta(tl.T5, tl.T71)) addEntry(tl.T73, "t7.3 new TCP up", fmtDelta(tl.T71, tl.T73)) if !tl.T74.IsZero() { addEntry(tl.T74, "t7.4 pre-readyz req ← BUG", fmtDelta(tl.T73, tl.T74)) @@ -1363,7 +1449,7 @@ func buildVerdict52(tl transitionTimeline) string { // master/control-plane nodes to match KAS topology. Includes tolerations for // both master and control-plane taints, and topologySpreadConstraints to // distribute pods across nodes. -func buildHealthserverDeployment(namespace, name string, replicas int32, startupDelay time.Duration, image string) *appsv1.Deployment { +func buildHealthserverDeployment(namespace, name string, replicas int32, startupDelay time.Duration, image string, aggregatorURL ...string) *appsv1.Deployment { labels := map[string]string{"app": name} return &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ @@ -1405,10 +1491,20 @@ func buildHealthserverDeployment(namespace, name string, replicas int32, startup Containers: []v1.Container{{ Name: "healthserver", Image: image, - Args: []string{ - fmt.Sprintf("--port=%d", healthserverPort), - fmt.Sprintf("--startup-delay=%s", startupDelay), - }, + Args: func() []string { + // Use the unified binary with "serve" subcommand. + // If aggregatorURL is provided, pass it so the server + // pushes lifecycle events to the aggregator. + args := []string{ + "serve", + fmt.Sprintf("--port=%d", healthserverPort), + fmt.Sprintf("--startup-delay=%s", startupDelay), + } + if len(aggregatorURL) > 0 && aggregatorURL[0] != "" { + args = append(args, fmt.Sprintf("--aggregator=%s", aggregatorURL[0])) + } + return args + }(), Ports: []v1.ContainerPort{{ Name: "http", ContainerPort: healthserverPort, @@ -1425,12 +1521,22 @@ func buildHealthserverDeployment(namespace, name string, replicas int32, startup Type: v1.SeccompProfileTypeRuntimeDefault, }, }, - Env: []v1.EnvVar{{ - Name: "POD_NAME", - ValueFrom: &v1.EnvVarSource{ - FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.name"}, + Env: []v1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.name"}, + }, }, - }}, + { + // POD_IP is used to register with the aggregator + // using the real node IP (hostNetwork pod). + Name: "POD_IP", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }, + }, }}, }, }, @@ -1515,5 +1621,252 @@ func grantHostNetworkSCC(ctx context.Context, cs clientset.Interface, namespace framework.ExpectNoError(err, "grant privileged SCC to default SA") } +// ─── In-cluster aggregator + client deployment ───────────────────────────── + +// deployAggregator creates a Pod and ClusterIP Service for the aggregator +// on a worker node. Returns the service DNS name for other pods to connect. +func deployAggregator(ctx context.Context, cs clientset.Interface, namespace, image string) string { + svcName := "healthtest-aggregator" + podName := "healthtest-aggregator" + + // Pod + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: namespace, + Labels: map[string]string{"app": "healthtest-aggregator"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{{ + Name: "aggregator", + Image: image, + Args: []string{"aggregator", fmt.Sprintf("--port=%d", aggregatorPort), "--scrape-interval=1s"}, + Ports: []v1.ContainerPort{{ + Name: "http", + ContainerPort: int32(aggregatorPort), + }}, + ReadinessProbe: &v1.Probe{ + ProbeHandler: v1.ProbeHandler{ + HTTPGet: &v1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt(aggregatorPort), + }, + }, + PeriodSeconds: 2, + }, + }}, + }, + } + _, err := cs.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create aggregator pod") + + // ClusterIP Service so servers and client can reach the aggregator by DNS + svc := &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: svcName, + Namespace: namespace, + }, + Spec: v1.ServiceSpec{ + Selector: map[string]string{"app": "healthtest-aggregator"}, + Ports: []v1.ServicePort{{ + Port: int32(aggregatorPort), + TargetPort: intstr.FromInt(aggregatorPort), + }}, + }, + } + _, err = cs.CoreV1().Services(namespace).Create(ctx, svc, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create aggregator service") + + // Wait for aggregator pod ready + err = wait.PollUntilContextTimeout(ctx, 2*time.Second, 2*time.Minute, true, func(ctx context.Context) (bool, error) { + p, err := cs.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + for _, c := range p.Status.Conditions { + if c.Type == v1.PodReady && c.Status == v1.ConditionTrue { + return true, nil + } + } + return false, nil + }) + framework.ExpectNoError(err, "aggregator pod ready") + + // Return the in-cluster DNS name for the aggregator service + return fmt.Sprintf("http://%s.%s.svc:%d", svcName, namespace, aggregatorPort) +} + +// deployInClusterClient creates a Pod on a worker node that sends HTTP +// requests to the NLB. Returns the pod name for result fetching. +func deployInClusterClient(ctx context.Context, cs clientset.Interface, namespace, image, nlbDNS, aggregatorURL string) string { + podName := "healthtest-client" + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: namespace, + Labels: map[string]string{"app": "healthtest-client"}, + }, + Spec: v1.PodSpec{ + // Schedule on worker nodes (NOT control-plane) + Affinity: &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{{ + MatchExpressions: []v1.NodeSelectorRequirement{{ + Key: "node-role.kubernetes.io/worker", + Operator: v1.NodeSelectorOpExists, + }}, + }}, + }, + }, + }, + Containers: []v1.Container{{ + Name: "client", + Image: image, + // POD_IP is used by the client to register with the + // aggregator using its real pod IP (not localhost). + Env: []v1.EnvVar{{ + Name: "POD_IP", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }}, + Args: []string{ + "client", + fmt.Sprintf("--url=http://%s:%d/", nlbDNS, healthserverPort), + fmt.Sprintf("--workers=%d", defaultClientWorkers), + fmt.Sprintf("--interval=%s", defaultClientInterval), + fmt.Sprintf("--port=%d", clientPort), + fmt.Sprintf("--aggregator=%s", aggregatorURL), + }, + Ports: []v1.ContainerPort{{ + Name: "http", + ContainerPort: int32(clientPort), + }}, + ReadinessProbe: &v1.Probe{ + ProbeHandler: v1.ProbeHandler{ + HTTPGet: &v1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt(clientPort), + }, + }, + PeriodSeconds: 2, + }, + }}, + }, + } + _, err := cs.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create client pod") + + // Wait for client pod ready (starts sending requests immediately) + err = wait.PollUntilContextTimeout(ctx, 2*time.Second, 2*time.Minute, true, func(ctx context.Context) (bool, error) { + p, err := cs.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + for _, c := range p.Status.Conditions { + if c.Type == v1.PodReady && c.Status == v1.ConditionTrue { + return true, nil + } + } + return false, nil + }) + framework.ExpectNoError(err, "client pod ready") + framework.Logf("[client-pod] started on worker node, sending requests to NLB") + + return podName +} + +// fetchClientRecords retrieves all request records from the in-cluster +// client pod via the K8s API server proxy. The client pod runs on a worker +// node with normal networking, so the API proxy works. +func fetchClientRecords(ctx context.Context, cs clientset.Interface, namespace, clientPodName string) []health.RequestRecord { + result := cs.CoreV1().RESTClient().Get(). + AbsPath(fmt.Sprintf("/api/v1/namespaces/%s/pods/%s:%d/proxy/records", namespace, clientPodName, clientPort)). + Timeout(30 * time.Second). + Do(ctx) + if err := result.Error(); err != nil { + framework.Logf("warning: failed to fetch client records: %v", err) + return nil + } + raw, err := result.Raw() + if err != nil { + framework.Logf("warning: failed to read client records: %v", err) + return nil + } + + // The client returns ClientRecord (types from the unified binary). + // Map to health.RequestRecord for compatibility with existing analysis. + type clientRecord struct { + Timestamp time.Time `json:"timestamp"` + TargetIP string `json:"target_ip"` + TCPDialDuration int64 `json:"tcp_dial_ms"` + HTTPStatus int `json:"http_status"` + ServerState string `json:"server_state"` + ServerID string `json:"server_id"` + FirstReadyzTime string `json:"first_readyz_time"` + IsNonReadyReq bool `json:"is_non_ready_req"` + Error string `json:"error,omitempty"` + } + var crs []clientRecord + if err := json.Unmarshal(raw, &crs); err != nil { + framework.Logf("warning: failed to parse client records: %v", err) + return nil + } + + records := make([]health.RequestRecord, len(crs)) + for i, cr := range crs { + records[i] = health.RequestRecord{ + Timestamp: cr.Timestamp, + TargetIP: cr.TargetIP, + TCPDialDuration: time.Duration(cr.TCPDialDuration) * time.Millisecond, + HTTPStatus: cr.HTTPStatus, + ServerState: cr.ServerState, + ServerID: cr.ServerID, + FirstReadyzTime: cr.FirstReadyzTime, + IsNonReadyReq: cr.IsNonReadyReq, + Error: cr.Error, + } + } + framework.Logf("[client-pod] fetched %d records from in-cluster client", len(records)) + return records +} + +// pushTGSnapshotToAggregator sends a TG health snapshot to the aggregator +// via K8s API proxy. Non-blocking — errors are logged but don't fail the test. +func pushTGSnapshotToAggregator(ctx context.Context, cs clientset.Interface, namespace string, snap health.TargetSnapshot) { + payload := struct { + Timestamp time.Time `json:"timestamp"` + Targets map[string]string `json:"targets"` + HealthyCount int `json:"healthy_count"` + UnhealthyCount int `json:"unhealthy_count"` + InitialCount int `json:"initial_count"` + }{ + Timestamp: snap.Timestamp, + Targets: snap.Targets, + HealthyCount: snap.HealthyCount, + UnhealthyCount: snap.UnhealthyCount, + InitialCount: snap.InitialCount, + } + data, _ := json.Marshal(payload) + cs.CoreV1().RESTClient().Post(). + AbsPath(fmt.Sprintf("/api/v1/namespaces/%s/pods/healthtest-aggregator:%d/proxy/tg-snapshot", namespace, aggregatorPort)). + Body(data). + Do(ctx) +} + +// fetchAggregatorTimeline retrieves the merged event timeline from the aggregator. +func fetchAggregatorTimeline(ctx context.Context, cs clientset.Interface, namespace string) []map[string]interface{} { + result := cs.CoreV1().RESTClient().Get(). + AbsPath(fmt.Sprintf("/api/v1/namespaces/%s/pods/healthtest-aggregator:%d/proxy/timeline", namespace, aggregatorPort)). + Timeout(30 * time.Second). + Do(ctx) + raw, _ := result.Raw() + var timeline []map[string]interface{} + json.Unmarshal(raw, &timeline) + return timeline +} + func ptrBool(b bool) *bool { return &b } func ptrInt64(i int64) *int64 { return &i } From db07955e35a59aa5cfed61500213fb76f0261ee0 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 13 Aug 2026 04:10:02 -0300 Subject: [PATCH 19/22] e2e: add CLB baseline comparison, fix verdict phase boundaries, rename tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Classic Load Balancer (CLB) variant of Scenario 5.5 to compare NLB and CLB health transition behavior using ELB v1 SDK (DescribeInstanceHealth). CLB states mapped to NLB terminology (InService→healthy, OutOfService→unhealthy) for consistent reporting. CLB Service config matches NLB for fair comparison: HTTP /readyz on port 19443, interval=10s, threshold=2/2 (CLB default unhealthy=6 overridden to 2). Fix [RESTART] verdict double-counting: previously counted from t7.1 (=t5, pod delete) which included expected GracefulShutdown draining traffic. Now counts from t7 (last routed request) — only requests AFTER the LB stopped routing are flagged. Rename test Contexts to prefix with LB type (NLB/CLB) instead of having "NLB" in the Describe block. Prevents confusing names like "NLB...CLB baseline". New dependency: github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing Co-Authored-By: Claude Opus 4.6 (1M context) --- .../e2e/aws/health/clb_observer.go | 200 + .../ccm-aws-tests/e2e/aws/helper.go | 56 + .../e2e/aws/lb_health_transition.go | 354 +- openshift-tests/ccm-aws-tests/go.mod | 9 +- openshift-tests/ccm-aws-tests/go.sum | 18 +- .../aws/aws-sdk-go-v2/aws/config.go | 12 + .../aws-sdk-go-v2/aws/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/aws/middleware/metadata.go | 17 +- .../aws/middleware/middleware.go | 47 +- .../aws/restrict_file_permissions.go | 21 + .../aws-sdk-go-v2/aws/retry/jitter_backoff.go | 77 +- .../aws/aws-sdk-go-v2/aws/retry/middleware.go | 137 +- .../aws/aws-sdk-go-v2/aws/retry/retry.go | 13 + .../aws/aws-sdk-go-v2/aws/retry/standard.go | 108 +- .../aws/transport/http/client.go | 27 +- .../smithy/v4signer_adapter_eventstream.go | 51 + .../internal/configsources/CHANGELOG.md | 62 + .../configsources/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/internal/context/context.go | 13 + .../internal/endpoints/v2/CHANGELOG.md | 62 + .../endpoints/v2/go_module_metadata.go | 2 +- .../service/elasticloadbalancing/CHANGELOG.md | 839 ++ .../service/elasticloadbalancing/LICENSE.txt | 202 + .../elasticloadbalancing/api_client.go | 971 ++ .../elasticloadbalancing/api_op_AddTags.go | 103 + ...pi_op_ApplySecurityGroupsToLoadBalancer.go | 105 + .../api_op_AttachLoadBalancerToSubnets.go | 105 + .../api_op_ConfigureHealthCheck.go | 104 + .../api_op_CreateAppCookieStickinessPolicy.go | 116 + .../api_op_CreateLBCookieStickinessPolicy.go | 118 + .../api_op_CreateLoadBalancer.go | 154 + .../api_op_CreateLoadBalancerListeners.go | 102 + .../api_op_CreateLoadBalancerPolicy.go | 107 + .../api_op_DeleteLoadBalancer.go | 98 + .../api_op_DeleteLoadBalancerListeners.go | 94 + .../api_op_DeleteLoadBalancerPolicy.go | 95 + ..._op_DeregisterInstancesFromLoadBalancer.go | 106 + .../api_op_DescribeAccountLimits.go | 100 + .../api_op_DescribeInstanceHealth.go | 707 ++ .../api_op_DescribeLoadBalancerAttributes.go | 94 + .../api_op_DescribeLoadBalancerPolicies.go | 99 + .../api_op_DescribeLoadBalancerPolicyTypes.go | 99 + .../api_op_DescribeLoadBalancers.go | 185 + .../api_op_DescribeTags.go | 94 + .../api_op_DetachLoadBalancerFromSubnets.go | 103 + ...DisableAvailabilityZonesForLoadBalancer.go | 111 + ..._EnableAvailabilityZonesForLoadBalancer.go | 107 + .../api_op_ModifyLoadBalancerAttributes.go | 122 + ...pi_op_RegisterInstancesWithLoadBalancer.go | 121 + .../elasticloadbalancing/api_op_RemoveTags.go | 96 + ...p_SetLoadBalancerListenerSSLCertificate.go | 106 + ...SetLoadBalancerPoliciesForBackendServer.go | 116 + ...pi_op_SetLoadBalancerPoliciesOfListener.go | 111 + .../service/elasticloadbalancing/auth.go | 355 + .../elasticloadbalancing/deserializers.go | 9861 +++++++++++++++++ .../service/elasticloadbalancing/doc.go | 33 + .../service/elasticloadbalancing/endpoints.go | 570 + .../elasticloadbalancing/generated.json | 62 + .../go_module_metadata.go | 6 + .../internal/endpoints/endpoints.go | 584 + .../service/elasticloadbalancing/options.go | 243 + .../elasticloadbalancing/serializers.go | 3041 +++++ .../elasticloadbalancing/types/errors.go | 591 + .../elasticloadbalancing/types/types.go | 579 + .../elasticloadbalancing/validators.go | 1260 +++ .../vendor/github.com/aws/smithy-go/AGENTS.md | 5 +- .../github.com/aws/smithy-go/CHANGELOG.md | 99 + .../vendor/github.com/aws/smithy-go/README.md | 39 +- .../aws/smithy-go/document/document.go | 124 +- .../aws/smithy-go/encoding/json/value.go | 5 + .../endpoints/private/bdd/evaluate.go | 35 + .../endpoints/private/rulesfn/string_slice.go | 18 + .../endpoints/private/rulesfn/uri.go | 3 + .../aws/smithy-go/eventstream/const.go | 24 + .../aws/smithy-go/eventstream/debug.go | 144 + .../aws/smithy-go/eventstream/decode.go | 218 + .../aws/smithy-go/eventstream/deserializer.go | 294 + .../aws/smithy-go/eventstream/encode.go | 167 + .../aws/smithy-go/eventstream/error.go | 23 + .../aws/smithy-go/eventstream/header.go | 175 + .../aws/smithy-go/eventstream/header_value.go | 521 + .../aws/smithy-go/eventstream/message.go | 99 + .../aws/smithy-go/eventstream/serializer.go | 228 + .../aws/smithy-go/eventstream/signer.go | 82 + .../aws/smithy-go/eventstream/types.go | 26 + .../aws/smithy-go/go_module_metadata.go | 2 +- .../vendor/github.com/aws/smithy-go/schema.go | 332 + .../github.com/aws/smithy-go/schema_ext.go | 38 + .../vendor/github.com/aws/smithy-go/serde.go | 229 + .../github.com/aws/smithy-go/sync/error.go | 53 + .../vendor/github.com/aws/smithy-go/trait.go | 21 + .../github.com/aws/smithy-go/traits/http.go | 69 + .../github.com/aws/smithy-go/traits/index.go | 107 + .../github.com/aws/smithy-go/traits/serde.go | 56 + .../github.com/aws/smithy-go/traits/traits.go | 72 + .../aws/smithy-go/transport/http/auth.go | 9 + .../smithy-go/transport/http/eventstream.go | 209 + .../transport/http/eventstream_middleware.go | 69 + .../aws/smithy-go/transport/http/host.go | 2 +- .../http/middleware_close_response_body.go | 23 + .../aws/smithy-go/transport/http/protocol.go | 27 + .../github.com/aws/smithy-go/type_registry.go | 70 + .../ccm-aws-tests/vendor/modules.txt | 17 +- 103 files changed, 27876 insertions(+), 123 deletions(-) create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/health/clb_observer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/CHANGELOG.md create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/LICENSE.txt create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_client.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AddTags.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ApplySecurityGroupsToLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AttachLoadBalancerToSubnets.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ConfigureHealthCheck.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateAppCookieStickinessPolicy.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLBCookieStickinessPolicy.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerListeners.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerPolicy.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerListeners.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerPolicy.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeregisterInstancesFromLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeAccountLimits.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeInstanceHealth.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerAttributes.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicies.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicyTypes.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancers.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeTags.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DetachLoadBalancerFromSubnets.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DisableAvailabilityZonesForLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_EnableAvailabilityZonesForLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ModifyLoadBalancerAttributes.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RegisterInstancesWithLoadBalancer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RemoveTags.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerListenerSSLCertificate.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesForBackendServer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesOfListener.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/auth.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/deserializers.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/doc.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/endpoints.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/generated.json create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/go_module_metadata.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/internal/endpoints/endpoints.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/options.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/serializers.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/errors.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/types.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/validators.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/const.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/debug.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/decode.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/deserializer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/encode.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/error.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header_value.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/message.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/serializer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/signer.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/types.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema_ext.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/serde.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/sync/error.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/trait.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/http.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/index.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/serde.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/traits.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/protocol.go create mode 100644 openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/type_registry.go diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/clb_observer.go b/openshift-tests/ccm-aws-tests/e2e/aws/health/clb_observer.go new file mode 100644 index 000000000..62d76a81a --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/clb_observer.go @@ -0,0 +1,200 @@ +package health + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" + "k8s.io/apimachinery/pkg/util/wait" +) + +// CLBObserver polls the Classic Load Balancer DescribeInstanceHealth API, +// recording state transitions per instance. CLB uses ELB v1 API with +// simpler health states: InService, OutOfService, Unknown. +type CLBObserver struct { + elbClient *elb.Client + lbName string + interval time.Duration + + mu sync.Mutex + events []HealthEvent + snapshots []TargetSnapshot + lastState map[string]string + + cancel context.CancelFunc +} + +// NewCLBObserver creates a CLB observer that polls instance health. +func NewCLBObserver(elbClient *elb.Client, lbName string, interval time.Duration) *CLBObserver { + return &CLBObserver{ + elbClient: elbClient, + lbName: lbName, + interval: interval, + lastState: make(map[string]string), + } +} + +// LBName returns the CLB name. +func (o *CLBObserver) LBName() string { return o.lbName } + +// mapCLBState maps CLB instance states to the common health state names +// used by the report (matching NLB terminology for consistent comparison). +func mapCLBState(clbState string) string { + switch clbState { + case "InService": + return "healthy" + case "OutOfService": + return "unhealthy" + case "Unknown": + return "initial" + default: + return clbState + } +} + +// WaitForAllHealthy blocks until all registered instances report InService. +func (o *CLBObserver) WaitForAllHealthy(ctx context.Context, minHealthy int, timeout time.Duration) error { + return wait.PollUntilContextTimeout(ctx, o.interval, timeout, true, func(ctx context.Context) (bool, error) { + output, err := o.elbClient.DescribeInstanceHealth(ctx, &elb.DescribeInstanceHealthInput{ + LoadBalancerName: aws.String(o.lbName), + }) + if err != nil { + return false, nil + } + healthy := 0 + for _, is := range output.InstanceStates { + if aws.ToString(is.State) == "InService" { + healthy++ + } + } + return healthy >= minHealthy, nil + }) +} + +// PollOnce performs a single DescribeInstanceHealth call and returns a +// TargetSnapshot (same format as NLB observer for consistent reporting). +func (o *CLBObserver) PollOnce(ctx context.Context) (TargetSnapshot, error) { + output, err := o.elbClient.DescribeInstanceHealth(ctx, &elb.DescribeInstanceHealthInput{ + LoadBalancerName: aws.String(o.lbName), + }) + if err != nil { + return TargetSnapshot{}, fmt.Errorf("describe instance health: %w", err) + } + + snap := TargetSnapshot{ + Timestamp: time.Now(), + Targets: make(map[string]string, len(output.InstanceStates)), + } + for _, is := range output.InstanceStates { + id := aws.ToString(is.InstanceId) + rawState := aws.ToString(is.State) + state := mapCLBState(rawState) + snap.Targets[id] = state + switch state { + case "healthy": + snap.HealthyCount++ + case "unhealthy": + snap.UnhealthyCount++ + case "initial": + snap.InitialCount++ + } + } + return snap, nil +} + +// Start begins polling DescribeInstanceHealth in a background goroutine. +func (o *CLBObserver) Start(ctx context.Context) { + ctx, o.cancel = context.WithCancel(ctx) + go o.pollLoop(ctx) +} + +// Stop cancels the background polling goroutine. +func (o *CLBObserver) Stop() { + if o.cancel != nil { + o.cancel() + } +} + +// Events returns a copy of all recorded health state transition events. +func (o *CLBObserver) Events() []HealthEvent { + o.mu.Lock() + defer o.mu.Unlock() + result := make([]HealthEvent, len(o.events)) + copy(result, o.events) + return result +} + +// Snapshots returns a copy of all per-poll full-state snapshots. +func (o *CLBObserver) Snapshots() []TargetSnapshot { + o.mu.Lock() + defer o.mu.Unlock() + result := make([]TargetSnapshot, len(o.snapshots)) + copy(result, o.snapshots) + return result +} + +func (o *CLBObserver) pollLoop(ctx context.Context) { + ticker := time.NewTicker(o.interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + o.pollOnce(ctx) + } + } +} + +func (o *CLBObserver) pollOnce(ctx context.Context) { + output, err := o.elbClient.DescribeInstanceHealth(ctx, &elb.DescribeInstanceHealthInput{ + LoadBalancerName: aws.String(o.lbName), + }) + if err != nil { + return + } + + o.mu.Lock() + defer o.mu.Unlock() + + now := time.Now() + + snap := TargetSnapshot{ + Timestamp: now, + Targets: make(map[string]string, len(output.InstanceStates)), + } + + for _, is := range output.InstanceStates { + id := aws.ToString(is.InstanceId) + rawState := aws.ToString(is.State) + state := mapCLBState(rawState) + + snap.Targets[id] = state + switch state { + case "healthy": + snap.HealthyCount++ + case "unhealthy": + snap.UnhealthyCount++ + case "initial": + snap.InitialCount++ + } + + prev := o.lastState[id] + if state != prev { + o.events = append(o.events, HealthEvent{ + Timestamp: now, + TargetID: id, + TargetPort: 0, // CLB doesn't report port per instance + State: state, + PrevState: prev, + Reason: rawState, // Keep original CLB state as reason + }) + o.lastState[id] = state + } + } + + o.snapshots = append(o.snapshots, snap) +} diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/helper.go b/openshift-tests/ccm-aws-tests/e2e/aws/helper.go index eab164d00..0bfcbb21e 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/helper.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/helper.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go-v2/config" ec2 "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/common" @@ -190,6 +191,61 @@ func securityGroupExists(ctx context.Context, ec2Client *ec2.Client, sgID string return true, nil } +// ─── CLB (Classic Load Balancer / ELB v1) helpers ─────────────────────────── + +// createAWSClientCLB creates an ELB v1 client for Classic Load Balancer operations. +func createAWSClientCLB(ctx context.Context) (*elb.Client, error) { + cfg, err := loadAWSConfig(ctx) + if err != nil { + return nil, err + } + return elb.NewFromConfig(cfg, func(o *elb.Options) { + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, 5) + }), nil +} + +// getCLBByDNSName finds a Classic Load Balancer by its DNS name. +// Returns the LB name (used for subsequent API calls) and the DNS name. +func getCLBByDNSName(ctx context.Context, elbClient *elb.Client, dnsName string) (string, error) { + var marker *string + for { + input := &elb.DescribeLoadBalancersInput{Marker: marker} + output, err := elbClient.DescribeLoadBalancers(ctx, input) + if err != nil { + return "", fmt.Errorf("describe CLBs: %w", err) + } + framework.Logf("found %d CLBs in page", len(output.LoadBalancerDescriptions)) + for _, lb := range output.LoadBalancerDescriptions { + if aws.ToString(lb.DNSName) == dnsName { + name := aws.ToString(lb.LoadBalancerName) + framework.Logf("found CLB %s with DNS %s", name, dnsName) + return name, nil + } + } + if output.NextMarker == nil { + break + } + marker = output.NextMarker + } + return "", fmt.Errorf("CLB with DNS %s not found", dnsName) +} + +// getCLBByDNSNameWithRetry retries getCLBByDNSName until the CLB is found +// or the timeout is reached. CLB provisioning can take time. +func getCLBByDNSNameWithRetry(ctx context.Context, elbClient *elb.Client, dnsName string) (string, error) { + var lbName string + err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 15*time.Minute, true, func(ctx context.Context) (bool, error) { + name, err := getCLBByDNSName(ctx, elbClient, dnsName) + if err != nil { + framework.Logf("CLB not found yet: %v", err) + return false, nil + } + lbName = name + return true, nil + }) + return lbName, err +} + // ec2IsNotFoundError checks if an error is an EC2 "not found" error. func ec2IsNotFoundError(err error) bool { if err == nil { diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index f6bdcbbbc..32ea71999 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -124,7 +124,7 @@ type serviceConfig struct { Topology string // e.g., "HighlyAvailable" } -var _ = Describe(healthTransitionTestPrefix+" NLB", func() { +var _ = Describe(healthTransitionTestPrefix, func() { f := framework.NewDefaultFramework("cloud-provider-aws") f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged @@ -137,7 +137,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { }) // ── Scenario 5.5 ─────────────────────────────────────────────────── - Context("pre-readyz routing detection (OCPBUGS-86789)", func() { + Context("NLB pre-readyz routing detection (OCPBUGS-86789)", func() { It("should not route to pre-readyz targets "+ "when healthy targets are available", func(ctx context.Context) { @@ -277,7 +277,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { // target_health_state.unhealthy.connection_termination.enabled = false // target_health_state.unhealthy.draining_interval_seconds = 300 // This simulates the NLB configuration applied by CAPA (OCPBUGS-55626). - Context("pre-readyz routing with CAPA TG attributes (OCPBUGS-86789)", func() { + Context("NLB pre-readyz routing with CAPA TG attributes (OCPBUGS-86789)", func() { It("should not route to pre-readyz targets "+ "with connection-termination disabled and draining=300s", func(ctx context.Context) { @@ -406,7 +406,7 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { }) // ── Scenario 5.2 ─────────────────────────────────────────────────── - Context("shutdown propagation measurement (SPLAT-307)", func() { + Context("NLB shutdown propagation measurement (SPLAT-307)", func() { It("should stop routing within shutdown-delay after "+ "readyz starts failing", func(ctx context.Context) { @@ -501,6 +501,124 @@ var _ = Describe(healthTransitionTestPrefix+" NLB", func() { framework.Logf("\n%s", report) }) }) + + // ── Scenario 5.5 CLB baseline ─────────────────────────────────────── + // Same as Scenario 5.5 but using Classic Load Balancer instead of NLB. + // Compares CLB and NLB health transition behavior to determine if the + // pre-readyz routing issue is NLB-specific (Hyperplane) or broader. + Context("CLB pre-readyz routing detection baseline (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "when healthy targets are available", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + replicas := int32(3) + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + + deployName := "healthserver" + svcName := "healthserver-lb" + + // Setup uses CLB (no nlb annotation) with same HC config + lbDNS, clbObserver, svcCfg, setupTimes, clientPodName := setupHealthTransitionCLB( + ctx, cs, ns, deployName, svcName, image, + replicas, startupDelay, + ) + _ = lbDNS + + clbObserver.Start(ctx) + // Push CLB health snapshots to aggregator every 2s + stopCLBPush := startCLBSnapshotPusher(ctx, cs, ns.Name, clbObserver) + framework.Logf("[observer] started CLB health polling (1s) + aggregator push (2s)") + framework.Logf("[client-pod] in-cluster client %s already sending requests", clientPodName) + defer func() { stopCLBPush(); clbObserver.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + framework.Logf("[steady] %d requests from in-cluster client, %d non-ready", len(steadyRecords), steadyNonReady) + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod") + newPod := waitForNewPod(ctx, cs, ns.Name, deployName, targetPod) + + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + } + + // Wait for CLB to detect unhealthy, then recover + By("waiting for CLB to detect unhealthy instance") + waitForCLBUnhealthy(ctx, clbObserver, 3*time.Minute) + + By("waiting for all CLB instances to become healthy") + err = clbObserver.WaitForAllHealthy(ctx, int(replicas), 10*time.Minute) + framework.ExpectNoError(err, "CLB instances healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) + allEvents := clbObserver.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-CLB (Pre-Readyz Routing CLB Baseline / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, clbObserver.Snapshots()) + + report += buildVerdict55(tl, allRecords) + + framework.Logf("\n%s", report) + }) + }) }) // ─── Setup helper ─────────────────────────────────────────────────────────── @@ -1365,25 +1483,25 @@ func buildVerdict55(tl transitionTimeline, records []health.RequestRecord) strin targetAfterShutdown++ } - // Count requests to the target pod's node during Restart phase (t7.1→t9). - // With externalTrafficPolicy: Local, instance target type, the target pod's - // node is the NLB target. Any request reaching that node's backend during - // restart means the NLB routed to an unhealthy target. + // Count requests to the target pod's node during Restart phase (t7→t9). + // Start from t7 (last routed request), NOT t7.1 (pod delete/SIGTERM), + // because requests between t5→t7 are expected GracefulShutdown traffic + // (NLB propagation delay) and are already reported by [SHUTDOWN]. + // Requests AFTER t7 mean the LB re-routed to the target unexpectedly. var targetDuringRestart int - if !tl.T71.IsZero() { + if !tl.T7.IsZero() { end := tl.T9 if end.IsZero() { end = tl.T10 } for _, r := range records { - if tl.T71.IsZero() || r.Timestamp.Before(tl.T71) { + if r.Timestamp.Before(tl.T7) { continue } if !end.IsZero() && r.Timestamp.After(end) { continue } - // Match the target pod OR the new pod (both run on the same node - // when the deployment reschedules to the same node) + // Match the target pod OR the new pod if r.ServerID == tl.TargetPod || r.ServerID == tl.NewPod { if r.ServerState == "pre-readyz" || r.ServerState == "draining" || r.ServerState == "shutdown" { targetDuringRestart++ @@ -1868,5 +1986,217 @@ func fetchAggregatorTimeline(ctx context.Context, cs clientset.Interface, namesp return timeline } +// ─── CLB (Classic Load Balancer) support ──────────────────────────────────── + +// setupHealthTransitionCLB creates the same infrastructure as setupHealthTransition +// but uses a Classic Load Balancer instead of NLB. The CLB observer uses the +// ELB v1 DescribeInstanceHealth API. Everything else (healthserver deployment, +// aggregator, in-cluster client) is identical. +func setupHealthTransitionCLB( + ctx context.Context, + cs clientset.Interface, + ns *v1.Namespace, + deployName, svcName, image string, + replicas int32, + startupDelay time.Duration, +) (lbDNS string, clbObserver *health.CLBObserver, cfg serviceConfig, setupTimes transitionTimeline, clientPodName string) { + + // Deploy aggregator first + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + framework.Logf("[aggregator] ready at %s", aggregatorURL) + + // SCC for hostNetwork + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + // Healthserver deployment (same as NLB) + By("creating healthserver Deployment (scheduled on master nodes, hostNetwork)") + deploy := buildHealthserverDeployment(ns.Name, deployName, replicas, startupDelay, image, aggregatorURL) + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().Deployments(ns.Name).Create(ctx, deploy, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create deployment") + + // CLB Service (no nlb annotation = CLB default) + By("creating CLB Service (master-only targets, cross-zone, /readyz HC)") + svc := buildHealthTransitionServiceCLB(ns.Name, svcName, deployName) + _, err = cs.CoreV1().Services(ns.Name).Create(ctx, svc, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create CLB service") + cfg.ServiceAnnotations = svc.Annotations + + cfg.Platform = "AWS" + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + cfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + cfg.Topology = "External (HyperShift)" + } else { + cfg.Topology = "HighlyAvailable" + } + } + + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up CLB health transition resources") + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, svcName, metav1.DeleteOptions{}) + _ = cs.AppsV1().Deployments(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-client", metav1.DeleteOptions{}) + if lbDNS != "" { + // CLB deletion is handled by cloud-provider-aws when the Service is deleted + waitForLBDeletion(cleanupCtx, lbDNS) + } + }) + + // Wait for deployment + By("waiting for Deployment rollout") + err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + d, err := cs.AppsV1().Deployments(ns.Name).Get(ctx, deployName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + framework.Logf("deployment ready replicas: %d/%d", d.Status.ReadyReplicas, replicas) + return d.Status.ReadyReplicas >= replicas, nil + }) + framework.ExpectNoError(err, "deployment rollout") + setupTimes.T1 = time.Now() + + // Wait for CLB provisioning + By("waiting for CLB provisioning") + err = wait.PollUntilContextTimeout(ctx, 10*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + s, err := cs.CoreV1().Services(ns.Name).Get(ctx, svcName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + if len(s.Status.LoadBalancer.Ingress) > 0 { + lbDNS = s.Status.LoadBalancer.Ingress[0].Hostname + return lbDNS != "", nil + } + return false, nil + }) + framework.ExpectNoError(err, "CLB provisioning") + setupTimes.T2 = time.Now() + cfg.LBDNS = lbDNS + + // Discover CLB by DNS name + By("discovering CLB by DNS name") + elbClient, err := createAWSClientCLB(ctx) + framework.ExpectNoError(err, "create CLB client") + + lbName, err := getCLBByDNSNameWithRetry(ctx, elbClient, lbDNS) + framework.ExpectNoError(err, "find CLB") + cfg.LBARN = lbName // CLB uses name, not ARN + cfg.TGTargetType = "instance (CLB)" + + // Create CLB observer + clbObserver = health.NewCLBObserver(elbClient, lbName, 1*time.Second) + + // Wait for all instances healthy + By("waiting for ALL CLB instances to become healthy") + err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + snap, pollErr := clbObserver.PollOnce(ctx) + if pollErr != nil { + return false, nil + } + total := snap.HealthyCount + snap.UnhealthyCount + snap.InitialCount + allHealthy := total > 0 && snap.UnhealthyCount == 0 && snap.InitialCount == 0 + if time.Now().Second()%10 == 0 { + var details []string + for id, state := range snap.Targets { + details = append(details, fmt.Sprintf("%s=%s", id, state)) + } + framework.Logf("[clb-wait] healthy=%d unhealthy=%d initial=%d total=%d | %s", + snap.HealthyCount, snap.UnhealthyCount, snap.InitialCount, total, + strings.Join(details, ", ")) + } + if allHealthy { + framework.Logf("[clb-wait] all %d instances healthy", snap.HealthyCount) + } + return allHealthy, nil + }) + framework.ExpectNoError(err, "all CLB instances healthy") + setupTimes.T3 = time.Now() + + // Deploy in-cluster client + By("deploying in-cluster client on worker node") + clientPodName = deployInClusterClient(ctx, cs, ns.Name, image, lbDNS, aggregatorURL) + + return lbDNS, clbObserver, cfg, setupTimes, clientPodName +} + +// buildHealthTransitionServiceCLB creates a Service for a Classic Load Balancer. +// CLB is the default when no aws-load-balancer-type annotation is set. +// HC annotations are set to match the NLB test for fair comparison: +// HTTP /readyz on port 19443, interval=10s, threshold=2/2. +func buildHealthTransitionServiceCLB(namespace, name, deployName string) *v1.Service { + return &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Annotations: map[string]string{ + // NO aws-load-balancer-type annotation = CLB (default) + "service.beta.kubernetes.io/aws-load-balancer-target-node-labels": "node-role.kubernetes.io/control-plane=", + "service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol": "HTTP", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-path": "/readyz", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": fmt.Sprintf("%d", healthserverPort), + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval": "10", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold": "2", + // CLB default unhealthy threshold is 6 — set to 2 for fair comparison with NLB + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold": "2", + }, + }, + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, + Selector: map[string]string{"app": deployName}, + Ports: []v1.ServicePort{{ + Name: "http", + Protocol: v1.ProtocolTCP, + Port: int32(healthserverPort), + TargetPort: intstr.FromInt(healthserverPort), + }}, + }, + } +} + +// waitForCLBUnhealthy blocks until at least one CLB instance reports OutOfService. +func waitForCLBUnhealthy(ctx context.Context, observer *health.CLBObserver, timeout time.Duration) { + _ = wait.PollUntilContextTimeout(ctx, 2*time.Second, timeout, true, func(ctx context.Context) (bool, error) { + snap, err := observer.PollOnce(ctx) + if err != nil { + return false, nil + } + if snap.UnhealthyCount > 0 { + framework.Logf("[clb-wait] detected %d unhealthy instance(s)", snap.UnhealthyCount) + return true, nil + } + return false, nil + }) +} + +// startCLBSnapshotPusher pushes CLB health snapshots to the aggregator every 2s. +func startCLBSnapshotPusher(ctx context.Context, cs clientset.Interface, namespace string, observer *health.CLBObserver) context.CancelFunc { + ctx, cancel := context.WithCancel(ctx) + go func() { + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + snap, err := observer.PollOnce(ctx) + if err != nil { + continue + } + pushTGSnapshotToAggregator(ctx, cs, namespace, snap) + } + } + }() + return cancel +} + func ptrBool(b bool) *bool { return &b } func ptrInt64(i int64) *int64 { return &i } diff --git a/openshift-tests/ccm-aws-tests/go.mod b/openshift-tests/ccm-aws-tests/go.mod index 3fe96a468..41c276f22 100644 --- a/openshift-tests/ccm-aws-tests/go.mod +++ b/openshift-tests/ccm-aws-tests/go.mod @@ -3,9 +3,10 @@ module github.com/openshift/cluster-cloud-controller-manager-operator/openshift- go 1.26.0 require ( - github.com/aws/aws-sdk-go-v2 v1.41.6 + github.com/aws/aws-sdk-go-v2 v1.43.5 github.com/aws/aws-sdk-go-v2/config v1.29.14 github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.0 + github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.36.5 github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.45.2 github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 @@ -28,15 +29,15 @@ require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.36 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.36 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect - github.com/aws/smithy-go v1.25.0 // indirect + github.com/aws/smithy-go v1.27.7 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect diff --git a/openshift-tests/ccm-aws-tests/go.sum b/openshift-tests/ccm-aws-tests/go.sum index 66e331587..f0782505c 100644 --- a/openshift-tests/ccm-aws-tests/go.sum +++ b/openshift-tests/ccm-aws-tests/go.sum @@ -6,22 +6,24 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg= -github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= +github.com/aws/aws-sdk-go-v2 v1.43.5 h1:yKT5GYnFWhuDo+DqKvE5ZPwVn3RjC4MAeBtZGlh6AVM= +github.com/aws/aws-sdk-go-v2 v1.43.5/go.mod h1:wZjAJppCntyOGgVSmgVTfDyRJK5PHOasO6Wsy8U7Axk= github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.36 h1:5CrzwxDqf4w3x1Vs3/NiZ0nsC34Hbm3pIDMWbsLebOE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.36/go.mod h1:A3gHdKZIvG/QXERzZwcxNS3RNDFcRCuhhTFBYp+V/nw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.36 h1:A4N2f4YPcST0v+dWtX+xrpPPCL9VTBhoIFFUWYqbacE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.36/go.mod h1:B/Qr859uxWUEfZeGotK5KAEoof4Q9YWgNtPSwV6jcyk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.0 h1:qTozRFl2YFFU2HJGl7ZAywlRQvBnAN591gbAFT5bE0s= github.com/aws/aws-sdk-go-v2/service/ec2 v1.299.0/go.mod h1:E1pnYwWFZ8N3REmeN9Fe/Zipbpps4HJj8DQGNnLUMYc= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.36.5 h1:z7y5fST1uH8JHTiiFfnS6OBTUZd9PldhYWO5tOmuHuY= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.36.5/go.mod h1:FDk9ENyByNCzprimqPD9FGn6i/kbLVgiG5jFwC543HQ= github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.45.2 h1:vX70Z4lNSr7XsioU0uJq5yvxgI50sB66MvD+V/3buS4= github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.45.2/go.mod h1:xnCC3vFBfOKpU6PcsCKL2ktgBTZfOwTGxj6V8/X3IS4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcbVcGABLOVuPYaIihj6IlkqubBwFj10K5fxRek= @@ -34,8 +36,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0c github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= -github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= -github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.7 h1:Zgj5z4LfcDYoQIVk+n/yGdTkP/2y6ZT5vYxe0fp7bqE= +github.com/aws/smithy-go v1.27.7/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go index 3219517da..0183a1222 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go @@ -164,6 +164,14 @@ type Config struct { // the shared config profile attribute request_min_compression_size_bytes RequestMinCompressSizeBytes int64 + // DisableClockSkewCorrection turns off SDK clock skew correction. When set + // the SDK will not adjust request signing timestamps to compensate for + // drift between the client and service clocks. Set to false (enabled) by + // default. This variable is sourced from the environment variable + // AWS_DISABLE_CLOCK_SKEW_CORRECTION or the shared config profile attribute + // disable_clock_skew_correction. + DisableClockSkewCorrection bool + // Controls how a resolved AWS account ID is handled for endpoint routing. AccountIDEndpointMode AccountIDEndpointMode @@ -204,6 +212,10 @@ type Config struct { // when constructing clients for specific services. Each callback function receives the service ID // and the service's Options struct, allowing for dynamic configuration based on the service. ServiceOptions []func(string, any) + + // Controls whether the SDK restricts file permissions on credential + // cache files it creates. + RestrictFilePermissions RestrictFilePermissions } // NewConfig returns a new Config pointer that can be chained with builder diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go index 236f2869f..7de2041f9 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go @@ -3,4 +3,4 @@ package aws // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.41.6" +const goModuleVersion = "1.43.5" diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/metadata.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/metadata.go index d66f0960a..ba2082411 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/metadata.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/metadata.go @@ -15,6 +15,8 @@ type RegisterServiceMetadata struct { SigningName string Region string OperationName string + + RequiresLegacyEndpoints bool } // ID returns the middleware identifier. @@ -33,10 +35,13 @@ func (s RegisterServiceMetadata) HandleInitialize( ctx = SetSigningName(ctx, s.SigningName) } if len(s.Region) > 0 { - ctx = setRegion(ctx, s.Region) + ctx = SetRegion(ctx, s.Region) } if len(s.OperationName) > 0 { - ctx = setOperationName(ctx, s.OperationName) + ctx = SetOperationName(ctx, s.OperationName) + } + if s.RequiresLegacyEndpoints { + ctx = SetRequiresLegacyEndpoints(ctx, true) } return next.HandleInitialize(ctx, in) } @@ -161,19 +166,19 @@ func SetServiceID(ctx context.Context, value string) context.Context { return middleware.WithStackValue(ctx, serviceIDKey{}, value) } -// setRegion sets the endpoint region on the context. +// SetRegion sets the endpoint region on the context. // // Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues // to clear all stack values. -func setRegion(ctx context.Context, value string) context.Context { +func SetRegion(ctx context.Context, value string) context.Context { return middleware.WithStackValue(ctx, regionKey{}, value) } -// setOperationName sets the service operation on the context. +// SetOperationName sets the service operation on the context. // // Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues // to clear all stack values. -func setOperationName(ctx context.Context, value string) context.Context { +func SetOperationName(ctx context.Context, value string) context.Context { return middleware.WithStackValue(ctx, operationNameKey{}, value) } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go index 6d5f0079c..3c4f2caec 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go @@ -43,7 +43,12 @@ func (r ClientRequestID) HandleBuild(ctx context.Context, in middleware.BuildInp } // RecordResponseTiming records the response timing for the SDK client requests. -type RecordResponseTiming struct{} +type RecordResponseTiming struct { + // DisableClockSkewCorrection suppresses recording of clock skew observed + // from the response, per the Clock Skew Correction SEP. Response timing is + // still recorded. + DisableClockSkewCorrection bool +} // ID is the middleware identifier func (a *RecordResponseTiming) ID() string { @@ -54,14 +59,17 @@ func (a *RecordResponseTiming) ID() string { func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( out middleware.DeserializeOutput, metadata middleware.Metadata, err error, ) { + requestAt := sdk.NowTime() out, metadata, err = next.HandleDeserialize(ctx, in) responseAt := sdk.NowTime() setResponseAt(&metadata, responseAt) var serverTime time.Time + var hasAgeHeader bool switch resp := out.RawResponse.(type) { case *smithyhttp.Response: + hasAgeHeader = len(resp.Header.Get("Age")) > 0 respDateHeader := resp.Header.Get("Date") if len(respDateHeader) == 0 { break @@ -77,14 +85,45 @@ func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middlewa setServerTime(&metadata, serverTime) } - if !serverTime.IsZero() { - attemptSkew := serverTime.Sub(responseAt) - setAttemptSkew(&metadata, attemptSkew) + if !a.DisableClockSkewCorrection { + if skew, ok := computeClockSkew(serverTime, requestAt, responseAt, hasAgeHeader); ok { + setAttemptSkew(&metadata, skew) + } } return out, metadata, err } +// maxTrustedRequestDuration bounds how long a request may take before the SDK +// discards the skew measurement derived from its response. A slower round trip +// could only produce a signing failure if it pushed the timestamp outside the +// SigV4 validity window. See the Clock Skew Correction SEP. +const maxTrustedRequestDuration = 15 * time.Minute + +// computeClockSkew derives a clock skew candidate from a response per the Clock +// Skew Correction SEP. It returns ok=false (no candidate) when the Date header +// was absent/unparseable (serverTime zero), the round trip exceeded the maximum +// trusted request duration, or the response was served from a cache (Age +// header present). Otherwise the skew is the difference between the server's +// Date and the midpoint of the request round trip. +func computeClockSkew(serverTime, requestAt, responseAt time.Time, hasAgeHeader bool) (time.Duration, bool) { + if serverTime.IsZero() { + return 0, false + } + + if hasAgeHeader { + return 0, false + } + + elapsed := responseAt.Sub(requestAt) + if elapsed > maxTrustedRequestDuration { + return 0, false + } + + midpoint := requestAt.Add(elapsed / 2) + return serverTime.Sub(midpoint), true +} + type responseAtKey struct{} // GetResponseAt returns the time response was received at. diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go new file mode 100644 index 000000000..6360b657b --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go @@ -0,0 +1,21 @@ +package aws + +// RestrictFilePermissions controls whether the SDK restricts file permissions +// on credential cache files it creates. +type RestrictFilePermissions string + +const ( + // RestrictFilePermissionsUnset indicates the setting has not been + // configured. + RestrictFilePermissionsUnset RestrictFilePermissions = "" + + // RestrictFilePermissionsUserReadWrite sets file permissions to owner + // read/write only (0600) and directory permissions to owner only (0700) + // when creating new cache files and directories on Unix. This is the + // default behavior. + RestrictFilePermissionsUserReadWrite RestrictFilePermissions = "user_read_write" + + // RestrictFilePermissionsUnrestricted does not set any file or directory + // permissions, relying on the system's default umask. + RestrictFilePermissionsUnrestricted RestrictFilePermissions = "unrestricted" +) diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go index c266996de..14225a53a 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go @@ -4,6 +4,7 @@ import ( "math" "time" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/internal/rand" "github.com/aws/aws-sdk-go-v2/internal/timeconv" ) @@ -12,9 +13,20 @@ import ( // number of attempts. type ExponentialJitterBackoff struct { maxBackoff time.Duration - // precomputed number of attempts needed to reach max backoff. + // precomputed number of attempts needed to reach max backoff (legacy mode). maxBackoffAttempts float64 + // Base delay for non-throttle errors (x in the formula t_i = b * min(x * r^i, MAX_BACKOFF)). + baseDelay time.Duration + + // Throttle error checker. When set and the error is a throttle, the base + // delay is 1s regardless of the configured baseDelay. + throttle IsErrorThrottle + + // When true, applies MAX_BACKOFF before jitter and uses throttle-aware + // base delay. + retries2026 bool + randFloat64 func() (float64, error) } @@ -25,13 +37,53 @@ func NewExponentialJitterBackoff(maxBackoff time.Duration) *ExponentialJitterBac maxBackoff: maxBackoff, maxBackoffAttempts: math.Log2( float64(maxBackoff) / float64(time.Second)), + baseDelay: time.Second, randFloat64: rand.CryptoRandFloat64, } } +// exponentialJitterBackoffOption is a functional option for ExponentialJitterBackoff. +type exponentialJitterBackoffOption func(*ExponentialJitterBackoff) + +// withBaseDelay sets the base delay for non-throttle errors. +func withBaseDelay(d time.Duration) exponentialJitterBackoffOption { + return func(j *ExponentialJitterBackoff) { + j.baseDelay = d + } +} + +// withThrottleCheck sets the throttle error checker used to determine if the +// backoff should use the throttle base delay (1s) instead of the configured +// base delay. +func withThrottleCheck(t IsErrorThrottle) exponentialJitterBackoffOption { + return func(j *ExponentialJitterBackoff) { + j.throttle = t + } +} + +// newExponentialJitterBackoffWithOptions returns an ExponentialJitterBackoff +// with the given options applied. +func newExponentialJitterBackoffWithOptions(maxBackoff time.Duration, optFns ...exponentialJitterBackoffOption) *ExponentialJitterBackoff { + j := NewExponentialJitterBackoff(maxBackoff) + j.retries2026 = true + for _, fn := range optFns { + fn(j) + } + return j +} + // BackoffDelay returns the duration to wait before the next attempt should be // made. Returns an error if unable get a duration. func (j *ExponentialJitterBackoff) BackoffDelay(attempt int, err error) (time.Duration, error) { + if j.retries2026 { + return j.backoffDelay2026(attempt, err) + } + return j.backoffDelayLegacy(attempt, err) +} + +// backoffDelayLegacy preserves the original backoff formula: b * 2^i, capped +// at maxBackoff. +func (j *ExponentialJitterBackoff) backoffDelayLegacy(attempt int, err error) (time.Duration, error) { if attempt > int(j.maxBackoffAttempts) { return j.maxBackoff, nil } @@ -47,3 +99,26 @@ func (j *ExponentialJitterBackoff) BackoffDelay(attempt int, err error) (time.Du return timeconv.FloatSecondsDur(delaySeconds), nil } + +// backoffDelay2026 uses throttle-aware base delay and applies MAX_BACKOFF +// before jitter: t_i = b * min(x * 2^i, MAX_BACKOFF). +func (j *ExponentialJitterBackoff) backoffDelay2026(attempt int, err error) (time.Duration, error) { + x := j.baseDelay + if j.throttle != nil && j.throttle.IsErrorThrottle(err) == aws.TrueTernary { + x = time.Second + } + + b, randErr := j.randFloat64() + if randErr != nil { + return 0, randErr + } + + ri := math.Pow(2, float64(attempt)) + delaySeconds := float64(x) / float64(time.Second) * ri + maxBackoffSeconds := float64(j.maxBackoff) / float64(time.Second) + if delaySeconds > maxBackoffSeconds { + delaySeconds = maxBackoffSeconds + } + + return timeconv.FloatSecondsDur(b * delaySeconds), nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go index 52acb62f9..126dcf47b 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go @@ -48,6 +48,12 @@ type Attempt struct { // call. ClientSkew *atomic.Int64 + // DisableClockSkewCorrection disables clock skew correction per the Clock + // Skew Correction SEP: observed skew is not applied to the signing + // timestamp, not recorded into ClientSkew, and clock skew error codes are + // not treated as retry candidates. + DisableClockSkewCorrection bool + retryer aws.RetryerV2 requestCloner RequestCloner } @@ -88,7 +94,7 @@ func (r *Attempt) HandleFinalize(ctx context.Context, in smithymiddle.FinalizeIn out smithymiddle.FinalizeOutput, metadata smithymiddle.Metadata, err error, ) { var attemptClockSkew time.Duration - if r.ClientSkew != nil { + if !r.DisableClockSkewCorrection && r.ClientSkew != nil { attemptClockSkew = time.Duration(r.ClientSkew.Load()) } @@ -159,7 +165,7 @@ func (r *Attempt) HandleFinalize(ctx context.Context, in smithymiddle.FinalizeIn // this guarantees we are staying on top of the persistent skew value // (either to apply it or to heal it back if the clocks realign) - if r.ClientSkew != nil { + if !r.DisableClockSkewCorrection && r.ClientSkew != nil { if resultSkew, ok := awsmiddle.GetAttemptSkew(metadata); ok { r.ClientSkew.Store(resultSkew.Nanoseconds()) } @@ -233,9 +239,11 @@ func (r *Attempt) handleAttempt( "failed to release retry token after request error, %w", err) } // Release the attempt token based on the state of the attempt's error (if any). - if releaseError := releaseAttemptToken(err); releaseError != nil && err != nil { - return out, attemptResult, nopRelease, fmt.Errorf( - "failed to release initial token after request error, %w", err) + if !newRetries2026() || attemptNum == 1 { + if releaseError := releaseAttemptToken(err); releaseError != nil && err != nil { + return out, attemptResult, nopRelease, fmt.Errorf( + "failed to release initial token after request error, %w", err) + } } // If there was no error making the attempt, nothing further to do. There // will be nothing to retry. @@ -243,7 +251,10 @@ func (r *Attempt) handleAttempt( return out, attemptResult, nopRelease, err } - err = wrapAsClockSkew(ctx, err) + if !r.DisableClockSkewCorrection { + candidateSkew, hasCandidateSkew := awsmiddle.GetAttemptSkew(metadata) + err = wrapAsClockSkew(err, candidateSkew, hasCandidateSkew, retryMetadata.AttemptClockSkew) + } //------------------------------ // Is Retryable and Should Retry @@ -276,6 +287,13 @@ func (r *Attempt) handleAttempt( // Get a retry token that will be released after the releaseRetryToken, retryTokenErr := r.retryer.GetRetryToken(ctx, err) if retryTokenErr != nil { + // Long-polling operations must still back off when quota is exceeded. + if newRetries2026() && internalcontext.GetIsLongPolling(ctx) { + if retryDelay, delayErr := r.retryer.RetryDelay(attemptNum-1, err); delayErr == nil { + retryDelay = adjustForRetryAfterHeader(retryDelay, err, logger, r.LogAttempts) + _ = sdk.SleepWithContext(ctx, retryDelay) + } + } return out, attemptResult, nopRelease, errors.Join(err, retryTokenErr) } @@ -285,10 +303,17 @@ func (r *Attempt) handleAttempt( // Get the retry delay before another attempt can be made, and sleep for // that time. Potentially early exist if the sleep is canceled via the // context. - retryDelay, reqErr := r.retryer.RetryDelay(attemptNum, err) + attempt := attemptNum + if newRetries2026() { + attempt = attemptNum - 1 + } + retryDelay, reqErr := r.retryer.RetryDelay(attempt, err) if reqErr != nil { return out, attemptResult, releaseRetryToken, reqErr } + if newRetries2026() { + retryDelay = adjustForRetryAfterHeader(retryDelay, err, logger, r.LogAttempts) + } if reqErr = sdk.SleepWithContext(ctx, retryDelay); reqErr != nil { err = &aws.RequestCanceledError{Err: reqErr} return out, attemptResult, releaseRetryToken, err @@ -300,37 +325,66 @@ func (r *Attempt) handleAttempt( return out, attemptResult, releaseRetryToken, err } -// errors that, if detected when we know there's a clock skew, -// can be retried and have a high chance of success -var possibleSkewCodes = map[string]struct{}{ +// clockSkewCodes are the error codes that may indicate a clock skew problem. +// Per the Clock Skew Correction SEP these are retryable only when the absolute +// skew observed from the response Date header exceeds the detection threshold. +// The SEP does not distinguish "definite" from "possible" skew errors: modern +// services overload a single code (e.g. InvalidSignatureException) for both +// skewed and genuinely malformed signatures, so every code is gated on the +// observed skew. +var clockSkewCodes = map[string]struct{}{ "InvalidSignatureException": {}, "SignatureDoesNotMatch": {}, "AuthFailure": {}, + "RequestTimeTooSkewed": {}, + "AccessDeniedException": {}, } -var definiteSkewCodes = map[string]struct{}{ - "RequestExpired": {}, - "RequestInTheFuture": {}, - "RequestTimeTooSkewed": {}, -} - -// wrapAsClockSkew checks if this error could be related to a clock skew -// error and if so, wrap the error. -func wrapAsClockSkew(ctx context.Context, err error) error { +// wrapAsClockSkew classifies err as a retryable clock skew error when its code +// is a known clock skew code and the signing time diverges from the server +// time by more than the detection threshold. +// +// The signing time is now() + attemptSkew. The server time is now() + +// candidateSkew (derived from the response Date header). The signing error is: +// +// |attemptSkew - candidateSkew| > skewThreshold +// +// This single check covers both fresh skew detection (attemptSkew is zero on +// first attempt, so the error equals |candidateSkew|) and stale offset healing +// (attemptSkew is large but the server and client clocks have realigned, so +// candidateSkew is near zero). +// +// If no candidate was observed (the Date header was absent, unparseable, or +// discarded as untrusted), the error is not treated as clock skew. +func wrapAsClockSkew(err error, candidateSkew time.Duration, hasCandidateSkew bool, attemptSkew time.Duration) error { var v interface{ ErrorCode() string } if !errors.As(err, &v) { return err } - if _, ok := definiteSkewCodes[v.ErrorCode()]; ok { - return &retryableClockSkewError{Err: err} + + if _, ok := clockSkewCodes[v.ErrorCode()]; !ok { + return err + } + + if !hasCandidateSkew { + return err } - _, isPossibleSkewCode := possibleSkewCodes[v.ErrorCode()] - if skew := internalcontext.GetAttemptSkewContext(ctx); skew > skewThreshold && isPossibleSkewCode { + + if absDuration(attemptSkew-candidateSkew) > skewThreshold { return &retryableClockSkewError{Err: err} } + return err } +func absDuration(d time.Duration) time.Duration { + if d < 0 { + return -d + } + + return d +} + // MetricsHeader attaches SDK request metric header for retries to the transport type MetricsHeader struct{} @@ -423,6 +477,43 @@ func AddRetryMiddlewares(stack *smithymiddle.Stack, options AddRetryMiddlewaresO return nil } +// adjustForRetryAfterHeader checks for the x-amz-retry-after response header +// and clamps the backoff duration accordingly. The header value is an integer +// representing milliseconds. The result is clamped to [t_i, 5s + t_i] where +// t_i is the jittered exponential backoff duration. Invalid header values are +// ignored. +func adjustForRetryAfterHeader(backoff time.Duration, err error, logger logging.Logger, logAttempts bool) time.Duration { + var re *http.ResponseError + if !errors.As(err, &re) || re.Response == nil || re.Response.Response == nil { + return backoff + } + + headerVal := re.Response.Header.Get("X-Amz-Retry-After") + if headerVal == "" { + return backoff + } + + ms, parseErr := strconv.ParseInt(headerVal, 10, 64) + if parseErr != nil || ms < 0 { + if logAttempts { + logger.Logf(logging.Debug, "ignoring invalid x-amz-retry-after header value %q", headerVal) + } + return backoff + } + + retryAfter := time.Duration(ms) * time.Millisecond + minDuration := backoff + maxDuration := 5*time.Second + backoff + + if retryAfter < minDuration { + return minDuration + } + if retryAfter > maxDuration { + return maxDuration + } + return retryAfter +} + // Determines the value of exception.type for metrics purposes. We prefer an // API-specific error code, otherwise it's just the Go type for the value. func errorType(err error) string { diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go index af81635b3..c240fb09b 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go @@ -72,6 +72,19 @@ func (r *withMaxBackoffDelay) RetryDelay(attempt int, err error) (time.Duration, return r.backoff.BackoffDelay(attempt, err) } +// AddWithLongPolling returns a retryer that is marked as long-polling. +// Long-polling operations will back off even when the retry quota is +// exhausted. +func AddWithLongPolling(r aws.Retryer) aws.Retryer { + return &withLongPolling{RetryerV2: wrapAsRetryerV2(r)} +} + +type withLongPolling struct { + aws.RetryerV2 +} + +func (w *withLongPolling) IsLongPolling() bool { return true } + type wrappedAsRetryerV2 struct { aws.Retryer } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go index d5ea93222..f2f9660da 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go @@ -3,6 +3,7 @@ package retry import ( "context" "fmt" + "os" "time" "github.com/aws/aws-sdk-go-v2/aws/ratelimit" @@ -35,8 +36,16 @@ const ( const ( DefaultRetryRateTokens uint = 500 DefaultRetryCost uint = 5 - DefaultRetryTimeoutCost uint = 10 DefaultNoRetryIncrement uint = 1 + + // DefaultRetryTimeoutCost is the cost to deduct from the RateLimiter's + // token bucket per retry caused by timeout error. + // + // When AWS_NEW_RETRIES_2026 is set to "true", timeouts are no longer + // treated differently than other transient errors. The discounted cost + // is instead applied to throttling errors via DefaultThrottlingRetryCost. + DefaultRetryTimeoutCost uint = 10 + DefaultThrottlingRetryCost uint = 5 ) // DefaultRetryableHTTPStatusCodes is the default set of HTTP status codes the SDK @@ -121,6 +130,12 @@ type StandardOptions struct { // It is safe to append to this list in NewStandard's functional options. Timeouts []IsErrorTimeout + // Set of strategies to determine if the attempt failed due to a throttle + // error. Used to determine the retry token cost. + // + // It is safe to append to this list in NewStandard's functional options. + Throttles []IsErrorThrottle + // Provides the rate limiting strategy for rate limiting attempt retries // across all attempts the retryer is being used with. // @@ -129,10 +144,14 @@ type StandardOptions struct { // consume more tokens than what's available results in operation failure. // The default implementation is parameterized as follows: // - a capacity of 500 (DefaultRetryRateTokens) - // - a retry caused by a timeout costs 10 tokens (DefaultRetryCost) - // - a retry caused by other errors costs 5 tokens (DefaultRetryTimeoutCost) + // - a retry caused by a timeout costs 10 tokens (DefaultRetryTimeoutCost) + // - a retry caused by other errors costs 5 tokens (DefaultRetryCost) // - an operation that succeeds on the 1st attempt adds 1 token (DefaultNoRetryIncrement) // + // When AWS_NEW_RETRIES_2026 is set to "true", the costs change: + // - a retry costs 14 tokens + // - a retry caused by a throttling error costs 5 tokens (DefaultThrottlingRetryCost) + // // You can disable rate limiting by setting this field to ratelimit.None. RateLimiter RateLimiter @@ -141,11 +160,23 @@ type StandardOptions struct { // The cost to deduct from the RateLimiter's token bucket per retry caused // by timeout error. + // + // When AWS_NEW_RETRIES_2026 is set to "true", this field is unused. + // Throttling errors use ThrottlingRetryCost instead. RetryTimeoutCost uint + // The cost to deduct from the RateLimiter's token bucket per retry caused + // by a throttling error. Only used when AWS_NEW_RETRIES_2026 is "true". + ThrottlingRetryCost uint + // The cost to payback to the RateLimiter's token bucket for successful // attempts. NoRetryIncrement uint + + // BaseDelay is the base backoff delay for non-throttle retryable errors. + // Throttling errors always use 1s. Defaults to 50ms if zero. + // Only used when AWS_NEW_RETRIES_2026 is "true"; ignored in legacy mode. + BaseDelay time.Duration } // RateLimiter provides the interface for limiting the rate of attempt retries @@ -161,6 +192,7 @@ type RateLimiter interface { type Standard struct { options StandardOptions + throttle IsErrorThrottle timeout IsErrorTimeout retryable IsErrorRetryable backoff BackoffDelayer @@ -169,17 +201,7 @@ type Standard struct { // NewStandard initializes a standard retry behavior with defaults that can be // overridden via functional options. func NewStandard(fnOpts ...func(*StandardOptions)) *Standard { - o := StandardOptions{ - MaxAttempts: DefaultMaxAttempts, - MaxBackoff: DefaultMaxBackoff, - Retryables: append([]IsErrorRetryable{}, DefaultRetryables...), - Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...), - - RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens), - RetryCost: DefaultRetryCost, - RetryTimeoutCost: DefaultRetryTimeoutCost, - NoRetryIncrement: DefaultNoRetryIncrement, - } + o := standardDefaults() for _, fn := range fnOpts { fn(&o) } @@ -189,13 +211,25 @@ func NewStandard(fnOpts ...func(*StandardOptions)) *Standard { backoff := o.Backoff if backoff == nil { - backoff = NewExponentialJitterBackoff(o.MaxBackoff) + if newRetries2026() { + baseDelay := o.BaseDelay + if baseDelay == 0 { + baseDelay = 50 * time.Millisecond + } + backoff = newExponentialJitterBackoffWithOptions(o.MaxBackoff, + withBaseDelay(baseDelay), + withThrottleCheck(IsErrorThrottles(o.Throttles)), + ) + } else { + backoff = NewExponentialJitterBackoff(o.MaxBackoff) + } } return &Standard{ options: o, backoff: backoff, retryable: IsErrorRetryables(o.Retryables), + throttle: IsErrorThrottles(o.Throttles), timeout: IsErrorTimeouts(o.Timeouts), } } @@ -244,8 +278,14 @@ func (s *Standard) noRetryIncrement() error { func (s *Standard) GetRetryToken(ctx context.Context, opErr error) (func(error) error, error) { cost := s.options.RetryCost - if s.timeout.IsErrorTimeout(opErr).Bool() { - cost = s.options.RetryTimeoutCost + if newRetries2026() { + if s.throttle.IsErrorThrottle(opErr).Bool() { + cost = s.options.ThrottlingRetryCost + } + } else { + if s.timeout.IsErrorTimeout(opErr).Bool() { + cost = s.options.RetryTimeoutCost + } } fn, err := s.options.RateLimiter.GetToken(ctx, cost) @@ -267,3 +307,37 @@ func (f releaseToken) release(err error) error { return f() } + +func newRetries2026() bool { + return os.Getenv("AWS_NEW_RETRIES_2026") == "true" +} + +func standardDefaults() StandardOptions { + if newRetries2026() { + return StandardOptions{ + MaxAttempts: DefaultMaxAttempts, + MaxBackoff: DefaultMaxBackoff, + Retryables: append([]IsErrorRetryable{}, DefaultRetryables...), + Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...), + Throttles: append([]IsErrorThrottle{}, DefaultThrottles...), + + RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens), + RetryCost: 14, + RetryTimeoutCost: DefaultRetryTimeoutCost, + ThrottlingRetryCost: DefaultThrottlingRetryCost, + NoRetryIncrement: DefaultNoRetryIncrement, + } + } + return StandardOptions{ + MaxAttempts: DefaultMaxAttempts, + MaxBackoff: DefaultMaxBackoff, + Retryables: append([]IsErrorRetryable{}, DefaultRetryables...), + Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...), + Throttles: append([]IsErrorThrottle{}, DefaultThrottles...), + + RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens), + RetryCost: DefaultRetryCost, + RetryTimeoutCost: DefaultRetryTimeoutCost, + NoRetryIncrement: DefaultNoRetryIncrement, + } +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/client.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/client.go index 49cc31205..94094ec2c 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/client.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/client.go @@ -2,6 +2,7 @@ package http import ( "context" + "crypto/fips140" "crypto/tls" "net" "net/http" @@ -27,6 +28,19 @@ var ( // Default to TLS 1.2 for all HTTPS requests. DefaultHTTPTransportTLSMinVersion uint16 = tls.VersionTLS12 + + // DefaultHTTPTransportTLSCurvePreferencesFIPS is the elliptic curve preference + // list applied to the default transport when the FIPS 140-3 module is active. + // + // Go's default preferences lead with X25519, which crypto/ecdh rejects under + // GODEBUG=fips140=only, failing every TLS handshake the SDK attempts. Only the + // NIST curves are FIPS-approved, so restricting to them keeps the default + // client usable in FIPS deployments. + DefaultHTTPTransportTLSCurvePreferencesFIPS = []tls.CurveID{ + tls.CurveP256, + tls.CurveP384, + tls.CurveP521, + } ) // Timeouts for net.Dialer's network connection. @@ -178,6 +192,16 @@ func defaultDialer() *net.Dialer { } } +// defaultTLSCurvePreferences returns the curve preferences for the default +// transport. Outside FIPS mode it returns nil so Go's own defaults apply, +// preserving X25519 and the post-quantum X25519MLKEM768 hybrid. +func defaultTLSCurvePreferences(fipsEnabled bool) []tls.CurveID { + if !fipsEnabled { + return nil + } + return DefaultHTTPTransportTLSCurvePreferencesFIPS +} + func defaultHTTPTransport() *http.Transport { dialer := defaultDialer() @@ -192,7 +216,8 @@ func defaultHTTPTransport() *http.Transport { ExpectContinueTimeout: DefaultHTTPTransportExpectContinueTimeout, ForceAttemptHTTP2: true, TLSClientConfig: &tls.Config{ - MinVersion: DefaultHTTPTransportTLSMinVersion, + MinVersion: DefaultHTTPTransportTLSMinVersion, + CurvePreferences: defaultTLSCurvePreferences(fips140.Enabled()), }, } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go new file mode 100644 index 000000000..320e88858 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go @@ -0,0 +1,51 @@ +package smithy + +import ( + "context" + "fmt" + "time" + + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + smithygo "github.com/aws/smithy-go" + "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/eventstream" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +var _ smithyhttp.EventStreamSigner = (*V4SignerAdapter)(nil) + +// NewMessageSigner implements [smithyhttp.EventStreamSigner]. +func (v *V4SignerAdapter) NewMessageSigner(ctx context.Context, r *smithyhttp.Request, identity auth.Identity, props smithygo.Properties) (eventstream.MessageSigner, error) { + ca, ok := identity.(*CredentialsAdapter) + if !ok { + return nil, fmt.Errorf("unexpected identity type: %T", identity) + } + + name, ok := smithyhttp.GetSigV4SigningName(&props) + if !ok { + return nil, fmt.Errorf("sigv4 signing name is required") + } + + region, ok := smithyhttp.GetSigV4SigningRegion(&props) + if !ok { + return nil, fmt.Errorf("sigv4 signing region is required") + } + + seed, err := v4.GetSignedRequestSignature(r.Request) + if err != nil { + return nil, fmt.Errorf("get seed signature: %w", err) + } + + return &streamSignerAdapter{ + signer: v4.NewStreamSigner(ca.Credentials, name, region, seed), + }, nil +} + +// streamSignerAdapter adapts v4.StreamSigner to eventstream.MessageSigner. +type streamSignerAdapter struct { + signer *v4.StreamSigner +} + +func (s *streamSignerAdapter) SignMessage(headers, payload []byte, signingTime time.Time) ([]byte, error) { + return s.signer.GetSignature(context.Background(), headers, payload, signingTime) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md index 9aa4e19e6..058dbb329 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md @@ -1,3 +1,65 @@ +# v1.4.36 (2026-08-10) + +* **Dependency Update**: Update to smithy-go v1.27.7. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.35 (2026-08-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.34 (2026-07-31.2) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.27.6 to fix various serde issues in HTTP binding services. + +# v1.4.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.30 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.29 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.28 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.27 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.26 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.25 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.24 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.23 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + # v1.4.22 (2026-04-17) * **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go index cd7837e2f..86c3a7098 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go @@ -3,4 +3,4 @@ package configsources // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.4.22" +const goModuleVersion = "1.4.36" diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go index f0c283d39..52f4ebc25 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go @@ -50,3 +50,16 @@ func GetAttemptSkewContext(ctx context.Context) time.Duration { x, _ := middleware.GetStackValue(ctx, clockSkew{}).(time.Duration) return x } + +type longPollingKey struct{} + +// SetIsLongPolling marks the operation as long-polling on the context. +func SetIsLongPolling(ctx context.Context, v bool) context.Context { + return middleware.WithStackValue(ctx, longPollingKey{}, v) +} + +// GetIsLongPolling returns whether the operation is long-polling. +func GetIsLongPolling(ctx context.Context) bool { + v, _ := middleware.GetStackValue(ctx, longPollingKey{}).(bool) + return v +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md index abb379a4d..8f4d52166 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md @@ -1,3 +1,65 @@ +# v2.7.36 (2026-08-10) + +* **Dependency Update**: Update to smithy-go v1.27.7. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.35 (2026-08-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.34 (2026-07-31.2) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.27.6 to fix various serde issues in HTTP binding services. + +# v2.7.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.30 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.29 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.28 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.27 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.26 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.25 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.24 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.23 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + # v2.7.22 (2026-04-17) * **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go index e295061a3..10aa62063 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go @@ -3,4 +3,4 @@ package endpoints // goModuleVersion is the tagged release for this module -const goModuleVersion = "2.7.22" +const goModuleVersion = "2.7.36" diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/CHANGELOG.md b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/CHANGELOG.md new file mode 100644 index 000000000..e67aedb15 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/CHANGELOG.md @@ -0,0 +1,839 @@ +# v1.36.5 (2026-08-10) + +* **Dependency Update**: Update to smithy-go v1.27.7. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.4 (2026-08-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.3 (2026-07-31.2) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.27.6 to fix various serde issues in HTTP binding services. + +# v1.36.2 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.1 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.0 (2026-07-21) + +* **Feature**: Add an option to clients to disable clock skew +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.1 (2026-07-13) + +* No change notes available for this release. + +# v1.35.0 (2026-07-06) + +* **Feature**: Add request serialization snapshot tests. + +# v1.34.8 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.34.7 (2026-06-29) + +* No change notes available for this release. + +# v1.34.6 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.34.5 (2026-06-05.2) + +* **Bug Fix**: Undo the initial wave of schema-serde releases due to several customer-reported regressions. + +# v1.34.4 (2026-06-04.2) + +* **Bug Fix**: Fixed a schema-serde bug where required, default-value input members weren't serialized. + +# v1.34.3 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.34.2 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.34.1 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.34.0 (2026-06-01) + +* **Feature**: Adding new BDD representation of endpoint ruleset + +# v1.33.27 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.26 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.25 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.24 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.23 (2026-03-26) + +* **Bug Fix**: Fix a bug where a recorded clock skew could persist on the client even if the client and server clock ended up realigning. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.22 (2026-03-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.21 (2026-03-03) + +* **Dependency Update**: Bump minimum Go version to 1.24 +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.20 (2026-02-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.19 (2026-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.18 (2025-12-09) + +* No change notes available for this release. + +# v1.33.17 (2025-12-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.16 (2025-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.24.0. Notably this version of the library reduces the allocation footprint of the middleware system. We observe a ~10% reduction in allocations per SDK call with this change. + +# v1.33.15 (2025-11-25) + +* **Bug Fix**: Add error check for endpoint param binding during auth scheme resolution to fix panic reported in #3234 + +# v1.33.14 (2025-11-19.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.13 (2025-11-12) + +* **Bug Fix**: Further reduce allocation overhead when the metrics system isn't in-use. +* **Bug Fix**: Reduce allocation overhead when the client doesn't have any HTTP interceptors configured. +* **Bug Fix**: Remove blank trace spans towards the beginning of the request that added no additional information. This conveys a slight reduction in overall allocations. + +# v1.33.12 (2025-11-11) + +* **Bug Fix**: Return validation error if input region is not a valid host label. + +# v1.33.11 (2025-11-04) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.23.2 which should convey some passive reduction of overall allocations, especially when not using the metrics system. + +# v1.33.10 (2025-10-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.9 (2025-10-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.8 (2025-10-22) + +* No change notes available for this release. + +# v1.33.7 (2025-10-16) + +* **Dependency Update**: Bump minimum Go version to 1.23. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.6 (2025-09-26) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.5 (2025-09-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.4 (2025-09-10) + +* No change notes available for this release. + +# v1.33.3 (2025-09-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.2 (2025-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.1 (2025-08-27) + +* **Dependency Update**: Update to smithy-go v1.23.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.0 (2025-08-22) + +* **Feature**: Remove incorrect endpoint tests + +# v1.32.2 (2025-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.1 (2025-08-20) + +* **Bug Fix**: Remove unused deserialization code. + +# v1.32.0 (2025-08-11) + +* **Feature**: Add support for configuring per-service Options via callback on global config. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.0 (2025-08-04) + +* **Feature**: Support configurable auth scheme preferences in service clients via AWS_AUTH_SCHEME_PREFERENCE in the environment, auth_scheme_preference in the config file, and through in-code settings on LoadDefaultConfig and client constructor methods. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.1 (2025-07-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.0 (2025-07-28) + +* **Feature**: Add support for HTTP interceptors. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.29.7 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.29.6 (2025-06-17) + +* **Dependency Update**: Update to smithy-go v1.22.4. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.29.5 (2025-06-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.29.4 (2025-06-06) + +* No change notes available for this release. + +# v1.29.3 (2025-04-10) + +* No change notes available for this release. + +# v1.29.2 (2025-04-03) + +* No change notes available for this release. + +# v1.29.1 (2025-03-04.2) + +* **Bug Fix**: Add assurance test for operation order. + +# v1.29.0 (2025-02-27) + +* **Feature**: Track credential providers via User-Agent Feature ids +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.18 (2025-02-18) + +* **Bug Fix**: Bump go version to 1.22 +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.17 (2025-02-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.16 (2025-02-04) + +* No change notes available for this release. + +# v1.28.15 (2025-01-31) + +* **Dependency Update**: Switch to code-generated waiter matchers, removing the dependency on go-jmespath. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.14 (2025-01-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.13 (2025-01-24) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.22.2. + +# v1.28.12 (2025-01-17) + +* **Bug Fix**: Fix bug where credentials weren't refreshed during retry loop. + +# v1.28.11 (2025-01-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.10 (2025-01-14) + +* **Bug Fix**: Fix issue where waiters were not failing on unmatched errors as they should. This may have breaking behavioral changes for users in fringe cases. See [this announcement](https://github.com/aws/aws-sdk-go-v2/discussions/2954) for more information. + +# v1.28.9 (2025-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.8 (2025-01-08) + +* No change notes available for this release. + +# v1.28.7 (2024-12-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.6 (2024-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.5 (2024-11-18) + +* **Dependency Update**: Update to smithy-go v1.22.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.4 (2024-11-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.3 (2024-10-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.2 (2024-10-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.1 (2024-10-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.28.0 (2024-10-04) + +* **Feature**: Add support for HTTP client metrics. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.27.4 (2024-10-03) + +* No change notes available for this release. + +# v1.27.3 (2024-09-27) + +* No change notes available for this release. + +# v1.27.2 (2024-09-25) + +* No change notes available for this release. + +# v1.27.1 (2024-09-23) + +* No change notes available for this release. + +# v1.27.0 (2024-09-20) + +* **Feature**: Add tracing and metrics support to service clients. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.8 (2024-09-17) + +* **Bug Fix**: **BREAKFIX**: Only generate AccountIDEndpointMode config for services that use it. This is a compiler break, but removes no actual functionality, as no services currently use the account ID in endpoint resolution. + +# v1.26.7 (2024-09-04) + +* No change notes available for this release. + +# v1.26.6 (2024-09-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.5 (2024-08-22) + +* No change notes available for this release. + +# v1.26.4 (2024-08-15) + +* **Dependency Update**: Bump minimum Go version to 1.21. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.3 (2024-07-10.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.2 (2024-07-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.1 (2024-06-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.26.0 (2024-06-26) + +* **Feature**: Support list-of-string endpoint parameter. + +# v1.25.1 (2024-06-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.25.0 (2024-06-18) + +* **Feature**: Track usage of various AWS SDK features in user-agent string. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.11 (2024-06-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.10 (2024-06-07) + +* **Bug Fix**: Add clock skew correction on all service clients +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.9 (2024-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.8 (2024-05-23) + +* No change notes available for this release. + +# v1.24.7 (2024-05-16) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.6 (2024-05-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.5 (2024-05-08) + +* **Bug Fix**: GoDoc improvement + +# v1.24.4 (2024-03-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.3 (2024-03-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.2 (2024-03-07) + +* **Bug Fix**: Remove dependency on go-cmp. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.1 (2024-02-23) + +* **Bug Fix**: Move all common, SDK-side middleware stack ops into the service client module to prevent cross-module compatibility issues in the future. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.24.0 (2024-02-22) + +* **Feature**: Add middleware stack snapshot tests. + +# v1.23.2 (2024-02-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.23.1 (2024-02-20) + +* **Bug Fix**: When sourcing values for a service's `EndpointParameters`, the lack of a configured region (i.e. `options.Region == ""`) will now translate to a `nil` value for `EndpointParameters.Region` instead of a pointer to the empty string `""`. This will result in a much more explicit error when calling an operation instead of an obscure hostname lookup failure. + +# v1.23.0 (2024-02-16) + +* **Feature**: Add new ClientOptions field to waiter config which allows you to extend the config for operation calls made by waiters. + +# v1.22.0 (2024-02-13) + +* **Feature**: Bump minimum Go version to 1.20 per our language support policy. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.7 (2024-01-04) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.6 (2023-12-20) + +* No change notes available for this release. + +# v1.21.5 (2023-12-08) + +* **Bug Fix**: Reinstate presence of default Retryer in functional options, but still respect max attempts set therein. + +# v1.21.4 (2023-12-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.3 (2023-12-06) + +* **Bug Fix**: Restore pre-refactor auth behavior where all operations could technically be performed anonymously. + +# v1.21.2 (2023-12-01) + +* **Bug Fix**: Correct wrapping of errors in authentication workflow. +* **Bug Fix**: Correctly recognize cache-wrapped instances of AnonymousCredentials at client construction. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.1 (2023-11-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.0 (2023-11-29) + +* **Feature**: Expose Options() accessor on service clients. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.5 (2023-11-28.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.4 (2023-11-28) + +* **Bug Fix**: Respect setting RetryMaxAttempts in functional options at client construction. + +# v1.20.3 (2023-11-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.2 (2023-11-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.1 (2023-11-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.0 (2023-11-01) + +* **Feature**: Adds support for configured endpoints via environment variables and the AWS shared configuration file. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.0 (2023-10-31) + +* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/). +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.0 (2023-10-24) + +* **Feature**: **BREAKFIX**: Correct nullability and default value representation of various input fields across a large number of services. Calling code that references one or more of the affected fields will need to update usage accordingly. See [2162](https://github.com/aws/aws-sdk-go-v2/issues/2162). + +# v1.17.2 (2023-10-12) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.1 (2023-10-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.0 (2023-09-18) + +* **Announcement**: [BREAKFIX] Change in MaxResults datatype from value to pointer type in cognito-sync service. +* **Feature**: Adds several endpoint ruleset changes across all models: smaller rulesets, removed non-unique regional endpoints, fixes FIPS and DualStack endpoints, and make region not required in SDK::Endpoint. Additional breakfix to cognito-sync field. + +# v1.16.5 (2023-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.4 (2023-08-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.3 (2023-08-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.2 (2023-08-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.1 (2023-08-01) + +* No change notes available for this release. + +# v1.16.0 (2023-07-31) + +* **Feature**: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.14 (2023-07-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.13 (2023-07-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.12 (2023-06-15) + +* No change notes available for this release. + +# v1.15.11 (2023-06-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.10 (2023-05-04) + +* No change notes available for this release. + +# v1.15.9 (2023-04-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.8 (2023-04-10) + +* No change notes available for this release. + +# v1.15.7 (2023-04-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.6 (2023-03-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.5 (2023-03-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.4 (2023-02-22) + +* **Bug Fix**: Prevent nil pointer dereference when retrieving error codes. + +# v1.15.3 (2023-02-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.2 (2023-02-03) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade smithy to 1.27.2 and correct empty query list serialization. + +# v1.15.1 (2023-01-23) + +* No change notes available for this release. + +# v1.15.0 (2023-01-05) + +* **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401). + +# v1.14.25 (2022-12-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.24 (2022-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.23 (2022-11-22) + +* No change notes available for this release. + +# v1.14.22 (2022-11-16) + +* No change notes available for this release. + +# v1.14.21 (2022-11-10) + +* No change notes available for this release. + +# v1.14.20 (2022-10-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.19 (2022-10-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.18 (2022-09-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.17 (2022-09-14) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.16 (2022-09-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.15 (2022-08-31) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.14 (2022-08-30) + +* No change notes available for this release. + +# v1.14.13 (2022-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.12 (2022-08-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.11 (2022-08-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.10 (2022-08-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.9 (2022-08-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.8 (2022-07-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.7 (2022-06-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.6 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.5 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.4 (2022-04-25) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.3 (2022-03-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.2 (2022-03-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.1 (2022-03-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.0 (2022-03-08) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.0 (2022-02-24) + +* **Feature**: API client updated +* **Feature**: Adds RetryMaxAttempts and RetryMod to API client Options. This allows the API clients' default Retryer to be configured from the shared configuration files or environment variables. Adding a new Retry mode of `Adaptive`. `Adaptive` retry mode is an experimental mode, adding client rate limiting when throttles reponses are received from an API. See [retry.AdaptiveMode](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/retry#AdaptiveMode) for more details, and configuration options. +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.12.0 (2022-01-14) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.11.0 (2022-01-07) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.10.0 (2021-12-21) + +* **Feature**: API Paginators now support specifying the initial starting token, and support stopping on empty string tokens. +* **Feature**: Updated to latest service endpoints + +# v1.9.2 (2021-12-02) + +* **Bug Fix**: Fixes a bug that prevented aws.EndpointResolverWithOptions from being used by the service client. ([#1514](https://github.com/aws/aws-sdk-go-v2/pull/1514)) +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.9.1 (2021-11-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.9.0 (2021-11-12) + +* **Feature**: Service clients now support custom endpoints that have an initial URI path defined. +* **Feature**: Waiters now have a `WaitForOutput` method, which can be used to retrieve the output of the successful wait operation. Thank you to [Andrew Haines](https://github.com/haines) for contributing this feature. + +# v1.8.0 (2021-11-06) + +* **Feature**: The SDK now supports configuration of FIPS and DualStack endpoints using environment variables, shared configuration, or programmatically. +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.7.0 (2021-10-21) + +* **Feature**: API client updated +* **Feature**: Updated to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.6.2 (2021-10-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.6.1 (2021-09-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.6.0 (2021-08-27) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.2 (2021-08-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.1 (2021-08-04) + +* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.0 (2021-07-15) + +* **Feature**: The ErrorCode method on generated service error types has been corrected to match the API model. +* **Documentation**: Updated service model to latest revision. +* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.0 (2021-06-25) + +* **Feature**: API client updated +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.1 (2021-05-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.0 (2021-05-14) + +* **Feature**: Constant has been added to modules to enable runtime version inspection for reporting. +* **Dependency Update**: Updated to the latest SDK module versions + diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/LICENSE.txt b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/LICENSE.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_client.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_client.go new file mode 100644 index 000000000..ba44e9b2c --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_client.go @@ -0,0 +1,971 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/defaults" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/retry" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + internalauthsmithy "github.com/aws/aws-sdk-go-v2/internal/auth/smithy" + internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources" + smithy "github.com/aws/smithy-go" + smithydocument "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/logging" + "github.com/aws/smithy-go/metrics" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net" + "net/http" + "sync/atomic" + "time" +) + +const ServiceID = "Elastic Load Balancing" +const ServiceAPIVersion = "2012-06-01" + +type operationMetrics struct { + Duration metrics.Float64Histogram + SerializeDuration metrics.Float64Histogram + ResolveIdentityDuration metrics.Float64Histogram + ResolveEndpointDuration metrics.Float64Histogram + SignRequestDuration metrics.Float64Histogram + DeserializeDuration metrics.Float64Histogram +} + +func (m *operationMetrics) histogramFor(name string) metrics.Float64Histogram { + switch name { + case "client.call.duration": + return m.Duration + case "client.call.serialization_duration": + return m.SerializeDuration + case "client.call.resolve_identity_duration": + return m.ResolveIdentityDuration + case "client.call.resolve_endpoint_duration": + return m.ResolveEndpointDuration + case "client.call.signing_duration": + return m.SignRequestDuration + case "client.call.deserialization_duration": + return m.DeserializeDuration + default: + panic("unrecognized operation metric") + } +} + +func timeOperationMetric[T any]( + ctx context.Context, metric string, fn func() (T, error), + opts ...metrics.RecordMetricOption, +) (T, error) { + mm := getOperationMetrics(ctx) + if mm == nil { // not using the metrics system + return fn() + } + + instr := mm.histogramFor(metric) + opts = append([]metrics.RecordMetricOption{withOperationMetadata(ctx)}, opts...) + + start := time.Now() + v, err := fn() + end := time.Now() + + elapsed := end.Sub(start) + instr.Record(ctx, float64(elapsed)/1e9, opts...) + return v, err +} + +func startMetricTimer(ctx context.Context, metric string, opts ...metrics.RecordMetricOption) func() { + mm := getOperationMetrics(ctx) + if mm == nil { // not using the metrics system + return func() {} + } + + instr := mm.histogramFor(metric) + opts = append([]metrics.RecordMetricOption{withOperationMetadata(ctx)}, opts...) + + var ended bool + start := time.Now() + return func() { + if ended { + return + } + ended = true + + end := time.Now() + + elapsed := end.Sub(start) + instr.Record(ctx, float64(elapsed)/1e9, opts...) + } +} + +func withOperationMetadata(ctx context.Context) metrics.RecordMetricOption { + return func(o *metrics.RecordMetricOptions) { + o.Properties.Set("rpc.service", middleware.GetServiceID(ctx)) + o.Properties.Set("rpc.method", middleware.GetOperationName(ctx)) + } +} + +type operationMetricsKey struct{} + +func withOperationMetrics(parent context.Context, mp metrics.MeterProvider) (context.Context, error) { + if _, ok := mp.(metrics.NopMeterProvider); ok { + // not using the metrics system - setting up the metrics context is a memory-intensive operation + // so we should skip it in this case + return parent, nil + } + + meter := mp.Meter("github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing") + om := &operationMetrics{} + + var err error + + om.Duration, err = operationMetricTimer(meter, "client.call.duration", + "Overall call duration (including retries and time to send or receive request and response body)") + if err != nil { + return nil, err + } + om.SerializeDuration, err = operationMetricTimer(meter, "client.call.serialization_duration", + "The time it takes to serialize a message body") + if err != nil { + return nil, err + } + om.ResolveIdentityDuration, err = operationMetricTimer(meter, "client.call.auth.resolve_identity_duration", + "The time taken to acquire an identity (AWS credentials, bearer token, etc) from an Identity Provider") + if err != nil { + return nil, err + } + om.ResolveEndpointDuration, err = operationMetricTimer(meter, "client.call.resolve_endpoint_duration", + "The time it takes to resolve an endpoint (endpoint resolver, not DNS) for the request") + if err != nil { + return nil, err + } + om.SignRequestDuration, err = operationMetricTimer(meter, "client.call.auth.signing_duration", + "The time it takes to sign a request") + if err != nil { + return nil, err + } + om.DeserializeDuration, err = operationMetricTimer(meter, "client.call.deserialization_duration", + "The time it takes to deserialize a message body") + if err != nil { + return nil, err + } + + return context.WithValue(parent, operationMetricsKey{}, om), nil +} + +func operationMetricTimer(m metrics.Meter, name, desc string) (metrics.Float64Histogram, error) { + return m.Float64Histogram(name, func(o *metrics.InstrumentOptions) { + o.UnitLabel = "s" + o.Description = desc + }) +} + +func getOperationMetrics(ctx context.Context) *operationMetrics { + if v := ctx.Value(operationMetricsKey{}); v != nil { + return v.(*operationMetrics) + } + return nil +} + +func operationTracer(p tracing.TracerProvider) tracing.Tracer { + return p.Tracer("github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing") +} + +// Client provides the API client to make operations call for Elastic Load +// Balancing. +type Client struct { + options Options + + // Difference between the time reported by the server and the client + timeOffset *atomic.Int64 +} + +// New returns an initialized Client based on the functional options. Provide +// additional functional options to further configure the behavior of the client, +// such as changing the client's endpoint or adding custom middleware behavior. +func New(options Options, optFns ...func(*Options)) *Client { + options = options.Copy() + + resolveDefaultLogger(&options) + + setResolvedDefaultsMode(&options) + + resolveRetryer(&options) + + resolveHTTPClient(&options) + + resolveHTTPSignerV4(&options) + + resolveEndpointResolverV2(&options) + + resolveTracerProvider(&options) + + resolveMeterProvider(&options) + + resolveAuthSchemeResolver(&options) + + for _, fn := range optFns { + fn(&options) + } + + finalizeRetryMaxAttempts(&options) + + ignoreAnonymousAuth(&options) + + wrapWithAnonymousAuth(&options) + + resolveAuthSchemes(&options) + + client := &Client{ + options: options, + } + + initializeTimeOffsetResolver(client) + + return client +} + +// Options returns a copy of the client configuration. +// +// Callers SHOULD NOT perform mutations on any inner structures within client +// config. Config overrides should instead be made on a per-operation basis through +// functional options. +func (c *Client) Options() Options { + return c.options.Copy() +} + +func (c *Client) invokeOperation( + ctx context.Context, opID string, params interface{}, optFns []func(*Options), stackFns ...func(*middleware.Stack, Options) error, +) ( + result interface{}, metadata middleware.Metadata, err error, +) { + ctx = middleware.ClearStackValues(ctx) + ctx = middleware.WithServiceID(ctx, ServiceID) + ctx = middleware.WithOperationName(ctx, opID) + + stack := middleware.NewStack(opID, smithyhttp.NewStackRequest) + options := c.options.Copy() + + for _, fn := range optFns { + fn(&options) + } + + finalizeOperationRetryMaxAttempts(&options, *c) + + finalizeClientEndpointResolverOptions(&options) + + ctx = setLoggerContext(ctx, options, opID) + + ctx = resolveServiceMetadata(ctx, options, opID) + + if err := c.addCommonMiddlewares(stack, options, opID); err != nil { + return nil, metadata, err + } + + for _, fn := range stackFns { + if err := fn(stack, options); err != nil { + return nil, metadata, err + } + } + + for _, fn := range options.APIOptions { + if err := fn(stack); err != nil { + return nil, metadata, err + } + } + + ctx, err = withOperationMetrics(ctx, options.MeterProvider) + if err != nil { + return nil, metadata, err + } + + tracer := operationTracer(options.TracerProvider) + spanName := fmt.Sprintf("%s.%s", ServiceID, opID) + + ctx = tracing.WithOperationTracer(ctx, tracer) + + ctx, span := tracer.StartSpan(ctx, spanName, func(o *tracing.SpanOptions) { + o.Kind = tracing.SpanKindClient + o.Properties.Set("rpc.system", "aws-api") + o.Properties.Set("rpc.method", opID) + o.Properties.Set("rpc.service", ServiceID) + }) + endTimer := startMetricTimer(ctx, "client.call.duration") + defer endTimer() + defer span.End() + + handler := smithyhttp.NewClientHandlerWithOptions(options.HTTPClient, func(o *smithyhttp.ClientHandler) { + o.Meter = options.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing") + }) + decorated := middleware.DecorateHandler(handler, stack) + result, metadata, err = decorated.Handle(ctx, params) + if err != nil { + span.SetProperty("exception.type", fmt.Sprintf("%T", err)) + span.SetProperty("exception.message", err.Error()) + + var aerr smithy.APIError + if errors.As(err, &aerr) { + span.SetProperty("api.error_code", aerr.ErrorCode()) + span.SetProperty("api.error_message", aerr.ErrorMessage()) + span.SetProperty("api.error_fault", aerr.ErrorFault().String()) + } + + err = &smithy.OperationError{ + ServiceID: ServiceID, + OperationName: opID, + Err: err, + } + } + + span.SetProperty("error", err != nil) + if err == nil { + span.SetStatus(tracing.SpanStatusOK) + } else { + span.SetStatus(tracing.SpanStatusError) + } + + return result, metadata, err +} + +type operationInputKey struct{} + +func setOperationInput(ctx context.Context, input interface{}) context.Context { + return middleware.WithStackValue(ctx, operationInputKey{}, input) +} + +func getOperationInput(ctx context.Context) interface{} { + return middleware.GetStackValue(ctx, operationInputKey{}) +} + +type setOperationInputMiddleware struct { +} + +func (*setOperationInputMiddleware) ID() string { + return "setOperationInput" +} + +func (m *setOperationInputMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + ctx = setOperationInput(ctx, in.Parameters) + return next.HandleSerialize(ctx, in) +} + +func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Finalize.Add(&resolveAuthSchemeMiddleware{operation: operation, options: options}, middleware.Before); err != nil { + return fmt.Errorf("add ResolveAuthScheme: %w", err) + } + if err := stack.Finalize.Insert(&getIdentityMiddleware{options: options}, "ResolveAuthScheme", middleware.After); err != nil { + return fmt.Errorf("add GetIdentity: %v", err) + } + if err := stack.Finalize.Insert(&resolveEndpointV2Middleware{options: options}, "GetIdentity", middleware.After); err != nil { + return fmt.Errorf("add ResolveEndpointV2: %v", err) + } + if err := stack.Finalize.Insert(&signRequestMiddleware{options: options}, "ResolveEndpointV2", middleware.After); err != nil { + return fmt.Errorf("add Signing: %w", err) + } + return nil +} + +func (c *Client) addCommonMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, operation); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + if err := addClientRequestID(stack); err != nil { + return err + } + if err := addRetry(stack, options, c); err != nil { + return err + } + if err := addRawResponseToMetadata(stack); err != nil { + return err + } + if err := addSpanRetryLoop(stack, options); err != nil { + return err + } + if err := addClientUserAgent(stack, options); err != nil { + return err + } + if err := addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err := addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err := addRecursionDetection(stack); err != nil { + return err + } + if err := addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err := addInterceptAttempt(stack, options); err != nil { + return err + } + return nil +} +func resolveAuthSchemeResolver(options *Options) { + if options.AuthSchemeResolver == nil { + options.AuthSchemeResolver = &defaultAuthSchemeResolver{} + } +} + +func resolveAuthSchemes(options *Options) { + if options.AuthSchemes == nil { + options.AuthSchemes = []smithyhttp.AuthScheme{ + internalauth.NewHTTPAuthScheme("aws.auth#sigv4", &internalauthsmithy.V4SignerAdapter{ + Signer: options.HTTPSignerV4, + Logger: options.Logger, + LogSigning: options.ClientLogMode.IsSigning(), + }), + } + } +} + +type noSmithyDocumentSerde = smithydocument.NoSerde + +func resolveDefaultLogger(o *Options) { + if o.Logger != nil { + return + } + o.Logger = logging.Nop{} +} + +func setLoggerContext(ctx context.Context, options Options, operation string) context.Context { + _ = operation + return middleware.SetLogger(ctx, options.Logger) +} + +func setResolvedDefaultsMode(o *Options) { + if len(o.resolvedDefaultsMode) > 0 { + return + } + + var mode aws.DefaultsMode + mode.SetFromString(string(o.DefaultsMode)) + + if mode == aws.DefaultsModeAuto { + mode = defaults.ResolveDefaultsModeAuto(o.Region, o.RuntimeEnvironment) + } + + o.resolvedDefaultsMode = mode +} + +// NewFromConfig returns a new client from the provided config. +func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { + opts := Options{ + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + DisableClockSkewCorrection: cfg.DisableClockSkewCorrection, + AuthSchemePreference: cfg.AuthSchemePreference, + } + resolveAWSRetryerProvider(cfg, &opts) + resolveAWSRetryMaxAttempts(cfg, &opts) + resolveAWSRetryMode(cfg, &opts) + resolveAWSEndpointResolver(cfg, &opts) + resolveInterceptors(cfg, &opts) + resolveUseDualStackEndpoint(cfg, &opts) + resolveUseFIPSEndpoint(cfg, &opts) + resolveBaseEndpoint(cfg, &opts) + return New(opts, func(o *Options) { + for _, opt := range cfg.ServiceOptions { + opt(ServiceID, o) + } + for _, opt := range optFns { + opt(o) + } + }) +} + +func resolveHTTPClient(o *Options) { + var buildable *awshttp.BuildableClient + + if o.HTTPClient != nil { + var ok bool + buildable, ok = o.HTTPClient.(*awshttp.BuildableClient) + if !ok { + return + } + } else { + buildable = awshttp.NewBuildableClient() + } + + modeConfig, err := defaults.GetModeConfiguration(o.resolvedDefaultsMode) + if err == nil { + buildable = buildable.WithDialerOptions(func(dialer *net.Dialer) { + if dialerTimeout, ok := modeConfig.GetConnectTimeout(); ok { + dialer.Timeout = dialerTimeout + } + }) + + buildable = buildable.WithTransportOptions(func(transport *http.Transport) { + if tlsHandshakeTimeout, ok := modeConfig.GetTLSNegotiationTimeout(); ok { + transport.TLSHandshakeTimeout = tlsHandshakeTimeout + } + }) + } + + o.HTTPClient = buildable +} + +func resolveRetryer(o *Options) { + if o.Retryer != nil { + return + } + + if len(o.RetryMode) == 0 { + modeConfig, err := defaults.GetModeConfiguration(o.resolvedDefaultsMode) + if err == nil { + o.RetryMode = modeConfig.RetryMode + } + } + if len(o.RetryMode) == 0 { + o.RetryMode = aws.RetryModeStandard + } + + var standardOptions []func(*retry.StandardOptions) + if v := o.RetryMaxAttempts; v != 0 { + standardOptions = append(standardOptions, func(so *retry.StandardOptions) { + so.MaxAttempts = v + }) + } + + switch o.RetryMode { + case aws.RetryModeAdaptive: + var adaptiveOptions []func(*retry.AdaptiveModeOptions) + if len(standardOptions) != 0 { + adaptiveOptions = append(adaptiveOptions, func(ao *retry.AdaptiveModeOptions) { + ao.StandardOptions = append(ao.StandardOptions, standardOptions...) + }) + } + o.Retryer = retry.NewAdaptiveMode(adaptiveOptions...) + + default: + o.Retryer = retry.NewStandard(standardOptions...) + } +} + +func resolveAWSRetryerProvider(cfg aws.Config, o *Options) { + if cfg.Retryer == nil { + return + } + o.Retryer = cfg.Retryer() +} + +func resolveAWSRetryMode(cfg aws.Config, o *Options) { + if len(cfg.RetryMode) == 0 { + return + } + o.RetryMode = cfg.RetryMode +} +func resolveAWSRetryMaxAttempts(cfg aws.Config, o *Options) { + if cfg.RetryMaxAttempts == 0 { + return + } + o.RetryMaxAttempts = cfg.RetryMaxAttempts +} + +func finalizeRetryMaxAttempts(o *Options) { + if o.RetryMaxAttempts == 0 { + return + } + + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, o.RetryMaxAttempts) +} + +func finalizeOperationRetryMaxAttempts(o *Options, client Client) { + if v := o.RetryMaxAttempts; v == 0 || v == client.options.RetryMaxAttempts { + return + } + + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, o.RetryMaxAttempts) +} + +func resolveAWSEndpointResolver(cfg aws.Config, o *Options) { + if cfg.EndpointResolver == nil && cfg.EndpointResolverWithOptions == nil { + return + } + o.EndpointResolver = withEndpointResolver(cfg.EndpointResolver, cfg.EndpointResolverWithOptions) +} + +func resolveInterceptors(cfg aws.Config, o *Options) { + o.Interceptors = cfg.Interceptors.Copy() +} + +func addClientUserAgent(stack *middleware.Stack, options Options) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + ua.AddSDKAgentKeyValue(awsmiddleware.APIMetadata, "elasticloadbalancing", goModuleVersion) + if len(options.AppID) > 0 { + ua.AddSDKAgentKey(awsmiddleware.ApplicationIdentifier, options.AppID) + } + + return nil +} + +func getOrAddRequestUserAgent(stack *middleware.Stack) (*awsmiddleware.RequestUserAgent, error) { + id := (*awsmiddleware.RequestUserAgent)(nil).ID() + mw, ok := stack.Build.Get(id) + if !ok { + mw = awsmiddleware.NewRequestUserAgent() + if err := stack.Build.Add(mw, middleware.After); err != nil { + return nil, err + } + } + + ua, ok := mw.(*awsmiddleware.RequestUserAgent) + if !ok { + return nil, fmt.Errorf("%T for %s middleware did not match expected type", mw, id) + } + + return ua, nil +} + +type HTTPSignerV4 interface { + SignHTTP(ctx context.Context, credentials aws.Credentials, r *http.Request, payloadHash string, service string, region string, signingTime time.Time, optFns ...func(*v4.SignerOptions)) error +} + +func resolveHTTPSignerV4(o *Options) { + if o.HTTPSignerV4 != nil { + return + } + o.HTTPSignerV4 = newDefaultV4Signer(*o) +} + +func newDefaultV4Signer(o Options) *v4.Signer { + return v4.NewSigner(func(so *v4.SignerOptions) { + so.Logger = o.Logger + so.LogSigning = o.ClientLogMode.IsSigning() + }) +} + +func addClientRequestID(stack *middleware.Stack) error { + return stack.Build.Add(&awsmiddleware.ClientRequestID{}, middleware.After) +} + +func addComputeContentLength(stack *middleware.Stack) error { + return stack.Build.Insert(&smithyhttp.ComputeContentLength{}, "ClientRequestID", middleware.After) +} + +func addRawResponseToMetadata(stack *middleware.Stack) error { + return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) +} + +func addRecordResponseTiming(stack *middleware.Stack, options Options) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{ + DisableClockSkewCorrection: options.DisableClockSkewCorrection, + }, middleware.After) +} + +func addSpanRetryLoop(stack *middleware.Stack, options Options) error { + return stack.Finalize.Insert(&spanRetryLoop{options: options}, "Retry", middleware.Before) +} + +type spanRetryLoop struct { + options Options +} + +func (*spanRetryLoop) ID() string { + return "spanRetryLoop" +} + +func (m *spanRetryLoop) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + middleware.FinalizeOutput, middleware.Metadata, error, +) { + tracer := operationTracer(m.options.TracerProvider) + ctx, span := tracer.StartSpan(ctx, "RetryLoop") + defer span.End() + + return next.HandleFinalize(ctx, in) +} +func addStreamingEventsPayload(stack *middleware.Stack) error { + return stack.Finalize.Add(&v4.StreamingEventsPayload{}, middleware.Before) +} + +func addUnsignedPayload(stack *middleware.Stack) error { + return stack.Finalize.Insert(&v4.UnsignedPayload{}, "ResolveEndpointV2", middleware.After) +} + +func addComputePayloadSHA256(stack *middleware.Stack) error { + return stack.Finalize.Insert(&v4.ComputePayloadSHA256{}, "ResolveEndpointV2", middleware.After) +} + +func addContentSHA256Header(stack *middleware.Stack) error { + return stack.Finalize.Insert(&v4.ContentSHA256Header{}, (*v4.ComputePayloadSHA256)(nil).ID(), middleware.After) +} + +func addIsWaiterUserAgent(o *Options) { + o.APIOptions = append(o.APIOptions, func(stack *middleware.Stack) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeatureWaiter) + return nil + }) +} + +func addIsPaginatorUserAgent(o *Options) { + o.APIOptions = append(o.APIOptions, func(stack *middleware.Stack) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeaturePaginator) + return nil + }) +} + +func addRetry(stack *middleware.Stack, o Options, c *Client) error { + attempt := retry.NewAttemptMiddleware(o.Retryer, smithyhttp.RequestCloner, func(m *retry.Attempt) { + m.LogAttempts = o.ClientLogMode.IsRetries() + m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing") + m.ClientSkew = c.timeOffset + m.DisableClockSkewCorrection = o.DisableClockSkewCorrection + }) + if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { + return err + } + if err := stack.Finalize.Insert(&retry.MetricsHeader{}, attempt.ID(), middleware.After); err != nil { + return err + } + return nil +} + +// resolves dual-stack endpoint configuration +func resolveUseDualStackEndpoint(cfg aws.Config, o *Options) error { + if len(cfg.ConfigSources) == 0 { + return nil + } + value, found, err := internalConfig.ResolveUseDualStackEndpoint(context.Background(), cfg.ConfigSources) + if err != nil { + return err + } + if found { + o.EndpointOptions.UseDualStackEndpoint = value + } + return nil +} + +// resolves FIPS endpoint configuration +func resolveUseFIPSEndpoint(cfg aws.Config, o *Options) error { + if len(cfg.ConfigSources) == 0 { + return nil + } + value, found, err := internalConfig.ResolveUseFIPSEndpoint(context.Background(), cfg.ConfigSources) + if err != nil { + return err + } + if found { + o.EndpointOptions.UseFIPSEndpoint = value + } + return nil +} + +func initializeTimeOffsetResolver(c *Client) { + c.timeOffset = new(atomic.Int64) +} + +func addUserAgentRetryMode(stack *middleware.Stack, options Options) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + switch options.Retryer.(type) { + case *retry.Standard: + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeatureRetryModeStandard) + case *retry.AdaptiveMode: + ua.AddUserAgentFeature(awsmiddleware.UserAgentFeatureRetryModeAdaptive) + } + return nil +} + +type setCredentialSourceMiddleware struct { + ua *awsmiddleware.RequestUserAgent + options Options +} + +func (m setCredentialSourceMiddleware) ID() string { return "SetCredentialSourceMiddleware" } + +func (m setCredentialSourceMiddleware) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) ( + out middleware.BuildOutput, metadata middleware.Metadata, err error, +) { + asProviderSource, ok := m.options.Credentials.(aws.CredentialProviderSource) + if !ok { + return next.HandleBuild(ctx, in) + } + providerSources := asProviderSource.ProviderSources() + for _, source := range providerSources { + m.ua.AddCredentialsSource(source) + } + return next.HandleBuild(ctx, in) +} + +func addCredentialSource(stack *middleware.Stack, options Options) error { + ua, err := getOrAddRequestUserAgent(stack) + if err != nil { + return err + } + + mw := setCredentialSourceMiddleware{ua: ua, options: options} + return stack.Build.Insert(&mw, "UserAgent", middleware.Before) +} + +func resolveTracerProvider(options *Options) { + if options.TracerProvider == nil { + options.TracerProvider = &tracing.NopTracerProvider{} + } +} + +func resolveMeterProvider(options *Options) { + if options.MeterProvider == nil { + options.MeterProvider = metrics.NopMeterProvider{} + } +} + +func resolveServiceMetadata(ctx context.Context, options Options, operation string) context.Context { + ctx = awsmiddleware.SetServiceID(ctx, ServiceID) + if options.Region != "" { + ctx = awsmiddleware.SetRegion(ctx, options.Region) + } + ctx = awsmiddleware.SetOperationName(ctx, operation) + if options.EndpointResolver != nil { + ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, true) + } + return ctx +} + +func addRecursionDetection(stack *middleware.Stack) error { + return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) +} + +func addRequestIDRetrieverMiddleware(stack *middleware.Stack) error { + return stack.Deserialize.Insert(&awsmiddleware.RequestIDRetriever{}, "OperationDeserializer", middleware.Before) + +} + +func addResponseErrorMiddleware(stack *middleware.Stack) error { + return stack.Deserialize.Insert(&awshttp.ResponseErrorWrapper{}, "RequestIDRetriever", middleware.Before) + +} + +func addRequestResponseLogging(stack *middleware.Stack, o Options) error { + return stack.Deserialize.Add(&smithyhttp.RequestResponseLogger{ + LogRequest: o.ClientLogMode.IsRequest(), + LogRequestWithBody: o.ClientLogMode.IsRequestWithBody(), + LogResponse: o.ClientLogMode.IsResponse(), + LogResponseWithBody: o.ClientLogMode.IsResponseWithBody(), + }, middleware.After) +} + +type disableHTTPSMiddleware struct { + DisableHTTPS bool +} + +func (*disableHTTPSMiddleware) ID() string { + return "disableHTTPS" +} + +func (m *disableHTTPSMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.DisableHTTPS && !smithyhttp.GetHostnameImmutable(ctx) { + req.URL.Scheme = "http" + } + + return next.HandleFinalize(ctx, in) +} + +func addDisableHTTPSMiddleware(stack *middleware.Stack, o Options) error { + return stack.Finalize.Insert(&disableHTTPSMiddleware{ + DisableHTTPS: o.EndpointOptions.DisableHTTPS, + }, "ResolveEndpointV2", middleware.After) +} + +func addInterceptBeforeRetryLoop(stack *middleware.Stack, opts Options) error { + return stack.Finalize.Insert(&smithyhttp.InterceptBeforeRetryLoop{ + Interceptors: opts.Interceptors.BeforeRetryLoop, + }, "Retry", middleware.Before) +} + +func addInterceptAttempt(stack *middleware.Stack, opts Options) error { + return stack.Finalize.Insert(&smithyhttp.InterceptAttempt{ + BeforeAttempt: opts.Interceptors.BeforeAttempt, + AfterAttempt: opts.Interceptors.AfterAttempt, + }, "Retry", middleware.After) +} + +func addInterceptors(stack *middleware.Stack, opts Options) error { + // middlewares are expensive, don't add all of these interceptor ones unless the caller + // actually has at least one interceptor configured + // + // at the moment it's all-or-nothing because some of the middlewares here are responsible for + // setting fields in the interceptor context for future ones + if len(opts.Interceptors.BeforeExecution) == 0 && + len(opts.Interceptors.BeforeSerialization) == 0 && len(opts.Interceptors.AfterSerialization) == 0 && + len(opts.Interceptors.BeforeRetryLoop) == 0 && + len(opts.Interceptors.BeforeAttempt) == 0 && + len(opts.Interceptors.BeforeSigning) == 0 && len(opts.Interceptors.AfterSigning) == 0 && + len(opts.Interceptors.BeforeTransmit) == 0 && len(opts.Interceptors.AfterTransmit) == 0 && + len(opts.Interceptors.BeforeDeserialization) == 0 && len(opts.Interceptors.AfterDeserialization) == 0 && + len(opts.Interceptors.AfterAttempt) == 0 && len(opts.Interceptors.AfterExecution) == 0 { + return nil + } + + return errors.Join( + stack.Initialize.Add(&smithyhttp.InterceptExecution{ + BeforeExecution: opts.Interceptors.BeforeExecution, + AfterExecution: opts.Interceptors.AfterExecution, + }, middleware.Before), + stack.Serialize.Insert(&smithyhttp.InterceptBeforeSerialization{ + Interceptors: opts.Interceptors.BeforeSerialization, + }, "OperationSerializer", middleware.Before), + stack.Serialize.Insert(&smithyhttp.InterceptAfterSerialization{ + Interceptors: opts.Interceptors.AfterSerialization, + }, "OperationSerializer", middleware.After), + stack.Finalize.Insert(&smithyhttp.InterceptBeforeSigning{ + Interceptors: opts.Interceptors.BeforeSigning, + }, "Signing", middleware.Before), + stack.Finalize.Insert(&smithyhttp.InterceptAfterSigning{ + Interceptors: opts.Interceptors.AfterSigning, + }, "Signing", middleware.After), + stack.Deserialize.Add(&smithyhttp.InterceptTransmit{ + BeforeTransmit: opts.Interceptors.BeforeTransmit, + AfterTransmit: opts.Interceptors.AfterTransmit, + }, middleware.After), + stack.Deserialize.Insert(&smithyhttp.InterceptBeforeDeserialization{ + Interceptors: opts.Interceptors.BeforeDeserialization, + }, "OperationDeserializer", middleware.After), // (deserialize stack is called in reverse) + stack.Deserialize.Insert(&smithyhttp.InterceptAfterDeserialization{ + Interceptors: opts.Interceptors.AfterDeserialization, + }, "OperationDeserializer", middleware.Before), + ) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AddTags.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AddTags.go new file mode 100644 index 000000000..e70a27355 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AddTags.go @@ -0,0 +1,103 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Adds the specified tags to the specified load balancer. Each load balancer can +// have a maximum of 10 tags. +// +// Each tag consists of a key and an optional value. If a tag with the same key is +// already associated with the load balancer, AddTags updates its value. +// +// For more information, see [Tag Your Classic Load Balancer] in the Classic Load Balancers Guide. +// +// [Tag Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/add-remove-tags.html +func (c *Client) AddTags(ctx context.Context, params *AddTagsInput, optFns ...func(*Options)) (*AddTagsOutput, error) { + if params == nil { + params = &AddTagsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "AddTags", params, optFns, c.addOperationAddTagsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*AddTagsOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for AddTags. +type AddTagsInput struct { + + // The name of the load balancer. You can specify one load balancer only. + // + // This member is required. + LoadBalancerNames []string + + // The tags. + // + // This member is required. + Tags []types.Tag + + noSmithyDocumentSerde +} + +// Contains the output of AddTags. +type AddTagsOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationAddTagsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpAddTags{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpAddTags{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpAddTagsValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ApplySecurityGroupsToLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ApplySecurityGroupsToLoadBalancer.go new file mode 100644 index 000000000..87890ba21 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ApplySecurityGroupsToLoadBalancer.go @@ -0,0 +1,105 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Associates one or more security groups with your load balancer in a virtual +// private cloud (VPC). The specified security groups override the previously +// associated security groups. +// +// For more information, see [Security Groups for Load Balancers in a VPC] in the Classic Load Balancers Guide. +// +// [Security Groups for Load Balancers in a VPC]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-groups.html#elb-vpc-security-groups +func (c *Client) ApplySecurityGroupsToLoadBalancer(ctx context.Context, params *ApplySecurityGroupsToLoadBalancerInput, optFns ...func(*Options)) (*ApplySecurityGroupsToLoadBalancerOutput, error) { + if params == nil { + params = &ApplySecurityGroupsToLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ApplySecurityGroupsToLoadBalancer", params, optFns, c.addOperationApplySecurityGroupsToLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ApplySecurityGroupsToLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for ApplySecurityGroupsToLoadBalancer. +type ApplySecurityGroupsToLoadBalancerInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The IDs of the security groups to associate with the load balancer. Note that + // you cannot specify the name of the security group. + // + // This member is required. + SecurityGroups []string + + noSmithyDocumentSerde +} + +// Contains the output of ApplySecurityGroupsToLoadBalancer. +type ApplySecurityGroupsToLoadBalancerOutput struct { + + // The IDs of the security groups associated with the load balancer. + SecurityGroups []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationApplySecurityGroupsToLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpApplySecurityGroupsToLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpApplySecurityGroupsToLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpApplySecurityGroupsToLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AttachLoadBalancerToSubnets.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AttachLoadBalancerToSubnets.go new file mode 100644 index 000000000..ab9b8f2b7 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_AttachLoadBalancerToSubnets.go @@ -0,0 +1,105 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Adds one or more subnets to the set of configured subnets for the specified +// load balancer. +// +// The load balancer evenly distributes requests across all registered subnets. +// For more information, see [Add or Remove Subnets for Your Load Balancer in a VPC]in the Classic Load Balancers Guide. +// +// [Add or Remove Subnets for Your Load Balancer in a VPC]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-manage-subnets.html +func (c *Client) AttachLoadBalancerToSubnets(ctx context.Context, params *AttachLoadBalancerToSubnetsInput, optFns ...func(*Options)) (*AttachLoadBalancerToSubnetsOutput, error) { + if params == nil { + params = &AttachLoadBalancerToSubnetsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "AttachLoadBalancerToSubnets", params, optFns, c.addOperationAttachLoadBalancerToSubnetsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*AttachLoadBalancerToSubnetsOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for AttachLoaBalancerToSubnets. +type AttachLoadBalancerToSubnetsInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The IDs of the subnets to add. You can add only one subnet per Availability + // Zone. + // + // This member is required. + Subnets []string + + noSmithyDocumentSerde +} + +// Contains the output of AttachLoadBalancerToSubnets. +type AttachLoadBalancerToSubnetsOutput struct { + + // The IDs of the subnets attached to the load balancer. + Subnets []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationAttachLoadBalancerToSubnetsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpAttachLoadBalancerToSubnets{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpAttachLoadBalancerToSubnets{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpAttachLoadBalancerToSubnetsValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ConfigureHealthCheck.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ConfigureHealthCheck.go new file mode 100644 index 000000000..21f430b12 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ConfigureHealthCheck.go @@ -0,0 +1,104 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Specifies the health check settings to use when evaluating the health state of +// your EC2 instances. +// +// For more information, see [Configure Health Checks for Your Load Balancer] in the Classic Load Balancers Guide. +// +// [Configure Health Checks for Your Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-healthchecks.html +func (c *Client) ConfigureHealthCheck(ctx context.Context, params *ConfigureHealthCheckInput, optFns ...func(*Options)) (*ConfigureHealthCheckOutput, error) { + if params == nil { + params = &ConfigureHealthCheckInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ConfigureHealthCheck", params, optFns, c.addOperationConfigureHealthCheckMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ConfigureHealthCheckOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for ConfigureHealthCheck. +type ConfigureHealthCheckInput struct { + + // The configuration information. + // + // This member is required. + HealthCheck *types.HealthCheck + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of ConfigureHealthCheck. +type ConfigureHealthCheckOutput struct { + + // The updated health check. + HealthCheck *types.HealthCheck + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationConfigureHealthCheckMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpConfigureHealthCheck{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpConfigureHealthCheck{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpConfigureHealthCheckValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateAppCookieStickinessPolicy.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateAppCookieStickinessPolicy.go new file mode 100644 index 000000000..c990700a5 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateAppCookieStickinessPolicy.go @@ -0,0 +1,116 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Generates a stickiness policy with sticky session lifetimes that follow that of +// an application-generated cookie. This policy can be associated only with +// HTTP/HTTPS listeners. +// +// This policy is similar to the policy created by CreateLBCookieStickinessPolicy, except that the lifetime of +// the special Elastic Load Balancing cookie, AWSELB , follows the lifetime of the +// application-generated cookie specified in the policy configuration. The load +// balancer only inserts a new stickiness cookie when the application response +// includes a new application cookie. +// +// If the application cookie is explicitly removed or expires, the session stops +// being sticky until a new application cookie is issued. +// +// For more information, see [Application-Controlled Session Stickiness] in the Classic Load Balancers Guide. +// +// [Application-Controlled Session Stickiness]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html#enable-sticky-sessions-application +func (c *Client) CreateAppCookieStickinessPolicy(ctx context.Context, params *CreateAppCookieStickinessPolicyInput, optFns ...func(*Options)) (*CreateAppCookieStickinessPolicyOutput, error) { + if params == nil { + params = &CreateAppCookieStickinessPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateAppCookieStickinessPolicy", params, optFns, c.addOperationCreateAppCookieStickinessPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateAppCookieStickinessPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for CreateAppCookieStickinessPolicy. +type CreateAppCookieStickinessPolicyInput struct { + + // The name of the application cookie used for stickiness. + // + // This member is required. + CookieName *string + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The name of the policy being created. Policy names must consist of alphanumeric + // characters and dashes (-). This name must be unique within the set of policies + // for this load balancer. + // + // This member is required. + PolicyName *string + + noSmithyDocumentSerde +} + +// Contains the output for CreateAppCookieStickinessPolicy. +type CreateAppCookieStickinessPolicyOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateAppCookieStickinessPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateAppCookieStickinessPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpCreateAppCookieStickinessPolicy{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateAppCookieStickinessPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLBCookieStickinessPolicy.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLBCookieStickinessPolicy.go new file mode 100644 index 000000000..ffafe31cb --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLBCookieStickinessPolicy.go @@ -0,0 +1,118 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Generates a stickiness policy with sticky session lifetimes controlled by the +// lifetime of the browser (user-agent) or a specified expiration period. This +// policy can be associated only with HTTP/HTTPS listeners. +// +// When a load balancer implements this policy, the load balancer uses a special +// cookie to track the instance for each request. When the load balancer receives a +// request, it first checks to see if this cookie is present in the request. If so, +// the load balancer sends the request to the application server specified in the +// cookie. If not, the load balancer sends the request to a server that is chosen +// based on the existing load-balancing algorithm. +// +// A cookie is inserted into the response for binding subsequent requests from the +// same user to that server. The validity of the cookie is based on the cookie +// expiration time, which is specified in the policy configuration. +// +// For more information, see [Duration-Based Session Stickiness] in the Classic Load Balancers Guide. +// +// [Duration-Based Session Stickiness]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html#enable-sticky-sessions-duration +func (c *Client) CreateLBCookieStickinessPolicy(ctx context.Context, params *CreateLBCookieStickinessPolicyInput, optFns ...func(*Options)) (*CreateLBCookieStickinessPolicyOutput, error) { + if params == nil { + params = &CreateLBCookieStickinessPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateLBCookieStickinessPolicy", params, optFns, c.addOperationCreateLBCookieStickinessPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateLBCookieStickinessPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for CreateLBCookieStickinessPolicy. +type CreateLBCookieStickinessPolicyInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The name of the policy being created. Policy names must consist of alphanumeric + // characters and dashes (-). This name must be unique within the set of policies + // for this load balancer. + // + // This member is required. + PolicyName *string + + // The time period, in seconds, after which the cookie should be considered stale. + // If you do not specify this parameter, the default value is 0, which indicates + // that the sticky session should last for the duration of the browser session. + CookieExpirationPeriod *int64 + + noSmithyDocumentSerde +} + +// Contains the output for CreateLBCookieStickinessPolicy. +type CreateLBCookieStickinessPolicyOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateLBCookieStickinessPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateLBCookieStickinessPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpCreateLBCookieStickinessPolicy{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateLBCookieStickinessPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancer.go new file mode 100644 index 000000000..005759bf1 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancer.go @@ -0,0 +1,154 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Creates a Classic Load Balancer. +// +// You can add listeners, security groups, subnets, and tags when you create your +// load balancer, or you can add them later using CreateLoadBalancerListeners, ApplySecurityGroupsToLoadBalancer, AttachLoadBalancerToSubnets, and AddTags. +// +// To describe your current load balancers, see DescribeLoadBalancers. When you are finished with a +// load balancer, you can delete it using DeleteLoadBalancer. +// +// You can create up to 20 load balancers per region per account. You can request +// an increase for the number of load balancers for your account. For more +// information, see [Limits for Your Classic Load Balancer]in the Classic Load Balancers Guide. +// +// [Limits for Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-limits.html +func (c *Client) CreateLoadBalancer(ctx context.Context, params *CreateLoadBalancerInput, optFns ...func(*Options)) (*CreateLoadBalancerOutput, error) { + if params == nil { + params = &CreateLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateLoadBalancer", params, optFns, c.addOperationCreateLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for CreateLoadBalancer. +type CreateLoadBalancerInput struct { + + // The listeners. + // + // For more information, see [Listeners for Your Classic Load Balancer] in the Classic Load Balancers Guide. + // + // [Listeners for Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html + // + // This member is required. + Listeners []types.Listener + + // The name of the load balancer. + // + // This name must be unique within your set of load balancers for the region, must + // have a maximum of 32 characters, must contain only alphanumeric characters or + // hyphens, and cannot begin or end with a hyphen. + // + // This member is required. + LoadBalancerName *string + + // One or more Availability Zones from the same region as the load balancer. + // + // You must specify at least one Availability Zone. + // + // You can add more Availability Zones after you create the load balancer using EnableAvailabilityZonesForLoadBalancer. + AvailabilityZones []string + + // The type of a load balancer. Valid only for load balancers in a VPC. + // + // By default, Elastic Load Balancing creates an Internet-facing load balancer + // with a DNS name that resolves to public IP addresses. For more information about + // Internet-facing and Internal load balancers, see [Load Balancer Scheme]in the Elastic Load Balancing + // User Guide. + // + // Specify internal to create a load balancer with a DNS name that resolves to + // private IP addresses. + // + // [Load Balancer Scheme]: https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html#load-balancer-scheme + Scheme *string + + // The IDs of the security groups to assign to the load balancer. + SecurityGroups []string + + // The IDs of the subnets in your VPC to attach to the load balancer. Specify one + // subnet per Availability Zone specified in AvailabilityZones . + Subnets []string + + // A list of tags to assign to the load balancer. + // + // For more information about tagging your load balancer, see [Tag Your Classic Load Balancer] in the Classic Load + // Balancers Guide. + // + // [Tag Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/add-remove-tags.html + Tags []types.Tag + + noSmithyDocumentSerde +} + +// Contains the output for CreateLoadBalancer. +type CreateLoadBalancerOutput struct { + + // The DNS name of the load balancer. + DNSName *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpCreateLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerListeners.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerListeners.go new file mode 100644 index 000000000..b5e1597e6 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerListeners.go @@ -0,0 +1,102 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Creates one or more listeners for the specified load balancer. If a listener +// with the specified port does not already exist, it is created; otherwise, the +// properties of the new listener must match the properties of the existing +// listener. +// +// For more information, see [Listeners for Your Classic Load Balancer] in the Classic Load Balancers Guide. +// +// [Listeners for Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html +func (c *Client) CreateLoadBalancerListeners(ctx context.Context, params *CreateLoadBalancerListenersInput, optFns ...func(*Options)) (*CreateLoadBalancerListenersOutput, error) { + if params == nil { + params = &CreateLoadBalancerListenersInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateLoadBalancerListeners", params, optFns, c.addOperationCreateLoadBalancerListenersMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateLoadBalancerListenersOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for CreateLoadBalancerListeners. +type CreateLoadBalancerListenersInput struct { + + // The listeners. + // + // This member is required. + Listeners []types.Listener + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the parameters for CreateLoadBalancerListener. +type CreateLoadBalancerListenersOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateLoadBalancerListenersMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateLoadBalancerListeners{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpCreateLoadBalancerListeners{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateLoadBalancerListenersValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerPolicy.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerPolicy.go new file mode 100644 index 000000000..f0efaba57 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_CreateLoadBalancerPolicy.go @@ -0,0 +1,107 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Creates a policy with the specified attributes for the specified load balancer. +// +// Policies are settings that are saved for your load balancer and that can be +// applied to the listener or the application server, depending on the policy type. +func (c *Client) CreateLoadBalancerPolicy(ctx context.Context, params *CreateLoadBalancerPolicyInput, optFns ...func(*Options)) (*CreateLoadBalancerPolicyOutput, error) { + if params == nil { + params = &CreateLoadBalancerPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateLoadBalancerPolicy", params, optFns, c.addOperationCreateLoadBalancerPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateLoadBalancerPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for CreateLoadBalancerPolicy. +type CreateLoadBalancerPolicyInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The name of the load balancer policy to be created. This name must be unique + // within the set of policies for this load balancer. + // + // This member is required. + PolicyName *string + + // The name of the base policy type. To get the list of policy types, use DescribeLoadBalancerPolicyTypes. + // + // This member is required. + PolicyTypeName *string + + // The policy attributes. + PolicyAttributes []types.PolicyAttribute + + noSmithyDocumentSerde +} + +// Contains the output of CreateLoadBalancerPolicy. +type CreateLoadBalancerPolicyOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateLoadBalancerPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateLoadBalancerPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpCreateLoadBalancerPolicy{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateLoadBalancerPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancer.go new file mode 100644 index 000000000..f263cd7d5 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancer.go @@ -0,0 +1,98 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Deletes the specified load balancer. +// +// If you are attempting to recreate a load balancer, you must reconfigure all +// settings. The DNS name associated with a deleted load balancer are no longer +// usable. The name and associated DNS record of the deleted load balancer no +// longer exist and traffic sent to any of its IP addresses is no longer delivered +// to your instances. +// +// If the load balancer does not exist or has already been deleted, the call to +// DeleteLoadBalancer still succeeds. +func (c *Client) DeleteLoadBalancer(ctx context.Context, params *DeleteLoadBalancerInput, optFns ...func(*Options)) (*DeleteLoadBalancerOutput, error) { + if params == nil { + params = &DeleteLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteLoadBalancer", params, optFns, c.addOperationDeleteLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DeleteLoadBalancer. +type DeleteLoadBalancerInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of DeleteLoadBalancer. +type DeleteLoadBalancerOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDeleteLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDeleteLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerListeners.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerListeners.go new file mode 100644 index 000000000..d983cf101 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerListeners.go @@ -0,0 +1,94 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Deletes the specified listeners from the specified load balancer. +func (c *Client) DeleteLoadBalancerListeners(ctx context.Context, params *DeleteLoadBalancerListenersInput, optFns ...func(*Options)) (*DeleteLoadBalancerListenersOutput, error) { + if params == nil { + params = &DeleteLoadBalancerListenersInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteLoadBalancerListeners", params, optFns, c.addOperationDeleteLoadBalancerListenersMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteLoadBalancerListenersOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DeleteLoadBalancerListeners. +type DeleteLoadBalancerListenersInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The client port numbers of the listeners. + // + // This member is required. + LoadBalancerPorts []int32 + + noSmithyDocumentSerde +} + +// Contains the output of DeleteLoadBalancerListeners. +type DeleteLoadBalancerListenersOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteLoadBalancerListenersMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDeleteLoadBalancerListeners{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDeleteLoadBalancerListeners{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteLoadBalancerListenersValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerPolicy.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerPolicy.go new file mode 100644 index 000000000..2958b3c6c --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeleteLoadBalancerPolicy.go @@ -0,0 +1,95 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Deletes the specified policy from the specified load balancer. This policy must +// not be enabled for any listeners. +func (c *Client) DeleteLoadBalancerPolicy(ctx context.Context, params *DeleteLoadBalancerPolicyInput, optFns ...func(*Options)) (*DeleteLoadBalancerPolicyOutput, error) { + if params == nil { + params = &DeleteLoadBalancerPolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteLoadBalancerPolicy", params, optFns, c.addOperationDeleteLoadBalancerPolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteLoadBalancerPolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DeleteLoadBalancerPolicy. +type DeleteLoadBalancerPolicyInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The name of the policy. + // + // This member is required. + PolicyName *string + + noSmithyDocumentSerde +} + +// Contains the output of DeleteLoadBalancerPolicy. +type DeleteLoadBalancerPolicyOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteLoadBalancerPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDeleteLoadBalancerPolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDeleteLoadBalancerPolicy{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeleteLoadBalancerPolicyValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeregisterInstancesFromLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeregisterInstancesFromLoadBalancer.go new file mode 100644 index 000000000..8e7bb741e --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DeregisterInstancesFromLoadBalancer.go @@ -0,0 +1,106 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Deregisters the specified instances from the specified load balancer. After the +// instance is deregistered, it no longer receives traffic from the load balancer. +// +// You can use DescribeLoadBalancers to verify that the instance is deregistered from the load balancer. +// +// For more information, see [Register or De-Register EC2 Instances] in the Classic Load Balancers Guide. +// +// [Register or De-Register EC2 Instances]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-deregister-register-instances.html +func (c *Client) DeregisterInstancesFromLoadBalancer(ctx context.Context, params *DeregisterInstancesFromLoadBalancerInput, optFns ...func(*Options)) (*DeregisterInstancesFromLoadBalancerOutput, error) { + if params == nil { + params = &DeregisterInstancesFromLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeregisterInstancesFromLoadBalancer", params, optFns, c.addOperationDeregisterInstancesFromLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeregisterInstancesFromLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DeregisterInstancesFromLoadBalancer. +type DeregisterInstancesFromLoadBalancerInput struct { + + // The IDs of the instances. + // + // This member is required. + Instances []types.Instance + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of DeregisterInstancesFromLoadBalancer. +type DeregisterInstancesFromLoadBalancerOutput struct { + + // The remaining instances registered with the load balancer. + Instances []types.Instance + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeregisterInstancesFromLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDeregisterInstancesFromLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDeregisterInstancesFromLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDeregisterInstancesFromLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeAccountLimits.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeAccountLimits.go new file mode 100644 index 000000000..3e389e68a --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeAccountLimits.go @@ -0,0 +1,100 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Describes the current Elastic Load Balancing resource limits for your AWS +// account. +// +// For more information, see [Limits for Your Classic Load Balancer] in the Classic Load Balancers Guide. +// +// [Limits for Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-limits.html +func (c *Client) DescribeAccountLimits(ctx context.Context, params *DescribeAccountLimitsInput, optFns ...func(*Options)) (*DescribeAccountLimitsOutput, error) { + if params == nil { + params = &DescribeAccountLimitsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeAccountLimits", params, optFns, c.addOperationDescribeAccountLimitsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeAccountLimitsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeAccountLimitsInput struct { + + // The marker for the next set of results. (You received this marker from a + // previous call.) + Marker *string + + // The maximum number of results to return with this call. + PageSize *int32 + + noSmithyDocumentSerde +} + +type DescribeAccountLimitsOutput struct { + + // Information about the limits. + Limits []types.Limit + + // The marker to use when requesting the next set of results. If there are no + // additional results, the string is empty. + NextMarker *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeAccountLimitsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeAccountLimits{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeAccountLimits{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeInstanceHealth.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeInstanceHealth.go new file mode 100644 index 000000000..7b620238f --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeInstanceHealth.go @@ -0,0 +1,707 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/middleware" + smithytime "github.com/aws/smithy-go/time" + smithywaiter "github.com/aws/smithy-go/waiter" + "time" +) + +// Describes the state of the specified instances with respect to the specified +// load balancer. If no instances are specified, the call describes the state of +// all instances that are currently registered with the load balancer. If instances +// are specified, their state is returned even if they are no longer registered +// with the load balancer. The state of terminated instances is not returned. +func (c *Client) DescribeInstanceHealth(ctx context.Context, params *DescribeInstanceHealthInput, optFns ...func(*Options)) (*DescribeInstanceHealthOutput, error) { + if params == nil { + params = &DescribeInstanceHealthInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeInstanceHealth", params, optFns, c.addOperationDescribeInstanceHealthMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeInstanceHealthOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DescribeInstanceHealth. +type DescribeInstanceHealthInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The IDs of the instances. + Instances []types.Instance + + noSmithyDocumentSerde +} + +// Contains the output for DescribeInstanceHealth. +type DescribeInstanceHealthOutput struct { + + // Information about the health of the instances. + InstanceStates []types.InstanceState + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeInstanceHealthMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeInstanceHealth{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeInstanceHealth{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeInstanceHealthValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// AnyInstanceInServiceWaiterOptions are waiter options for +// AnyInstanceInServiceWaiter +type AnyInstanceInServiceWaiterOptions struct { + + // Set of options to modify how an operation is invoked. These apply to all + // operations invoked for this client. Use functional options on operation call to + // modify this list for per operation behavior. + // + // Passing options here is functionally equivalent to passing values to this + // config's ClientOptions field that extend the inner client's APIOptions directly. + APIOptions []func(*middleware.Stack) error + + // Functional options to be passed to all operations invoked by this client. + // + // Function values that modify the inner APIOptions are applied after the waiter + // config's own APIOptions modifiers. + ClientOptions []func(*Options) + + // MinDelay is the minimum amount of time to delay between retries. If unset, + // AnyInstanceInServiceWaiter will use default minimum delay of 15 seconds. Note + // that MinDelay must resolve to a value lesser than or equal to the MaxDelay. + MinDelay time.Duration + + // MaxDelay is the maximum amount of time to delay between retries. If unset or + // set to zero, AnyInstanceInServiceWaiter will use default max delay of 120 + // seconds. Note that MaxDelay must resolve to value greater than or equal to the + // MinDelay. + MaxDelay time.Duration + + // LogWaitAttempts is used to enable logging for waiter retry attempts + LogWaitAttempts bool + + // Retryable is function that can be used to override the service defined + // waiter-behavior based on operation output, or returned error. This function is + // used by the waiter to decide if a state is retryable or a terminal state. + // + // By default service-modeled logic will populate this option. This option can + // thus be used to define a custom waiter state with fall-back to service-modeled + // waiter state mutators.The function returns an error in case of a failure state. + // In case of retry state, this function returns a bool value of true and nil + // error, while in case of success it returns a bool value of false and nil error. + Retryable func(context.Context, *DescribeInstanceHealthInput, *DescribeInstanceHealthOutput, error) (bool, error) +} + +// AnyInstanceInServiceWaiter defines the waiters for AnyInstanceInService +type AnyInstanceInServiceWaiter struct { + client DescribeInstanceHealthAPIClient + + options AnyInstanceInServiceWaiterOptions +} + +// NewAnyInstanceInServiceWaiter constructs a AnyInstanceInServiceWaiter. +func NewAnyInstanceInServiceWaiter(client DescribeInstanceHealthAPIClient, optFns ...func(*AnyInstanceInServiceWaiterOptions)) *AnyInstanceInServiceWaiter { + options := AnyInstanceInServiceWaiterOptions{} + options.MinDelay = 15 * time.Second + options.MaxDelay = 120 * time.Second + options.Retryable = anyInstanceInServiceStateRetryable + + for _, fn := range optFns { + fn(&options) + } + return &AnyInstanceInServiceWaiter{ + client: client, + options: options, + } +} + +// Wait calls the waiter function for AnyInstanceInService waiter. The maxWaitDur +// is the maximum wait duration the waiter will wait. The maxWaitDur is required +// and must be greater than zero. +func (w *AnyInstanceInServiceWaiter) Wait(ctx context.Context, params *DescribeInstanceHealthInput, maxWaitDur time.Duration, optFns ...func(*AnyInstanceInServiceWaiterOptions)) error { + _, err := w.WaitForOutput(ctx, params, maxWaitDur, optFns...) + return err +} + +// WaitForOutput calls the waiter function for AnyInstanceInService waiter and +// returns the output of the successful operation. The maxWaitDur is the maximum +// wait duration the waiter will wait. The maxWaitDur is required and must be +// greater than zero. +func (w *AnyInstanceInServiceWaiter) WaitForOutput(ctx context.Context, params *DescribeInstanceHealthInput, maxWaitDur time.Duration, optFns ...func(*AnyInstanceInServiceWaiterOptions)) (*DescribeInstanceHealthOutput, error) { + if maxWaitDur <= 0 { + return nil, fmt.Errorf("maximum wait time for waiter must be greater than zero") + } + + options := w.options + for _, fn := range optFns { + fn(&options) + } + + if options.MaxDelay <= 0 { + options.MaxDelay = 120 * time.Second + } + + if options.MinDelay > options.MaxDelay { + return nil, fmt.Errorf("minimum waiter delay %v must be lesser than or equal to maximum waiter delay of %v.", options.MinDelay, options.MaxDelay) + } + + ctx, cancelFn := context.WithTimeout(ctx, maxWaitDur) + defer cancelFn() + + logger := smithywaiter.Logger{} + remainingTime := maxWaitDur + + var attempt int64 + for { + + attempt++ + apiOptions := options.APIOptions + start := time.Now() + + if options.LogWaitAttempts { + logger.Attempt = attempt + apiOptions = append([]func(*middleware.Stack) error{}, options.APIOptions...) + apiOptions = append(apiOptions, logger.AddLogger) + } + + out, err := w.client.DescribeInstanceHealth(ctx, params, func(o *Options) { + baseOpts := []func(*Options){ + addIsWaiterUserAgent, + } + o.APIOptions = append(o.APIOptions, apiOptions...) + for _, opt := range baseOpts { + opt(o) + } + for _, opt := range options.ClientOptions { + opt(o) + } + }) + + retryable, err := options.Retryable(ctx, params, out, err) + if err != nil { + return nil, err + } + if !retryable { + return out, nil + } + + remainingTime -= time.Since(start) + if remainingTime < options.MinDelay || remainingTime <= 0 { + break + } + + // compute exponential backoff between waiter retries + delay, err := smithywaiter.ComputeDelay( + attempt, options.MinDelay, options.MaxDelay, remainingTime, + ) + if err != nil { + return nil, fmt.Errorf("error computing waiter delay, %w", err) + } + + remainingTime -= delay + // sleep for the delay amount before invoking a request + if err := smithytime.SleepWithContext(ctx, delay); err != nil { + return nil, fmt.Errorf("request cancelled while waiting, %w", err) + } + } + return nil, fmt.Errorf("exceeded max wait time for AnyInstanceInService waiter") +} + +func anyInstanceInServiceStateRetryable(ctx context.Context, input *DescribeInstanceHealthInput, output *DescribeInstanceHealthOutput, err error) (bool, error) { + + if err == nil { + v1 := output.InstanceStates + var v2 []string + for _, v := range v1 { + v3 := v.State + if v3 != nil { + v2 = append(v2, *v3) + } + } + expectedValue := "InService" + var match bool + for _, v := range v2 { + if string(v) == expectedValue { + match = true + break + } + } + + if match { + return false, nil + } + } + + if err != nil { + return false, err + } + return true, nil +} + +// InstanceDeregisteredWaiterOptions are waiter options for +// InstanceDeregisteredWaiter +type InstanceDeregisteredWaiterOptions struct { + + // Set of options to modify how an operation is invoked. These apply to all + // operations invoked for this client. Use functional options on operation call to + // modify this list for per operation behavior. + // + // Passing options here is functionally equivalent to passing values to this + // config's ClientOptions field that extend the inner client's APIOptions directly. + APIOptions []func(*middleware.Stack) error + + // Functional options to be passed to all operations invoked by this client. + // + // Function values that modify the inner APIOptions are applied after the waiter + // config's own APIOptions modifiers. + ClientOptions []func(*Options) + + // MinDelay is the minimum amount of time to delay between retries. If unset, + // InstanceDeregisteredWaiter will use default minimum delay of 15 seconds. Note + // that MinDelay must resolve to a value lesser than or equal to the MaxDelay. + MinDelay time.Duration + + // MaxDelay is the maximum amount of time to delay between retries. If unset or + // set to zero, InstanceDeregisteredWaiter will use default max delay of 120 + // seconds. Note that MaxDelay must resolve to value greater than or equal to the + // MinDelay. + MaxDelay time.Duration + + // LogWaitAttempts is used to enable logging for waiter retry attempts + LogWaitAttempts bool + + // Retryable is function that can be used to override the service defined + // waiter-behavior based on operation output, or returned error. This function is + // used by the waiter to decide if a state is retryable or a terminal state. + // + // By default service-modeled logic will populate this option. This option can + // thus be used to define a custom waiter state with fall-back to service-modeled + // waiter state mutators.The function returns an error in case of a failure state. + // In case of retry state, this function returns a bool value of true and nil + // error, while in case of success it returns a bool value of false and nil error. + Retryable func(context.Context, *DescribeInstanceHealthInput, *DescribeInstanceHealthOutput, error) (bool, error) +} + +// InstanceDeregisteredWaiter defines the waiters for InstanceDeregistered +type InstanceDeregisteredWaiter struct { + client DescribeInstanceHealthAPIClient + + options InstanceDeregisteredWaiterOptions +} + +// NewInstanceDeregisteredWaiter constructs a InstanceDeregisteredWaiter. +func NewInstanceDeregisteredWaiter(client DescribeInstanceHealthAPIClient, optFns ...func(*InstanceDeregisteredWaiterOptions)) *InstanceDeregisteredWaiter { + options := InstanceDeregisteredWaiterOptions{} + options.MinDelay = 15 * time.Second + options.MaxDelay = 120 * time.Second + options.Retryable = instanceDeregisteredStateRetryable + + for _, fn := range optFns { + fn(&options) + } + return &InstanceDeregisteredWaiter{ + client: client, + options: options, + } +} + +// Wait calls the waiter function for InstanceDeregistered waiter. The maxWaitDur +// is the maximum wait duration the waiter will wait. The maxWaitDur is required +// and must be greater than zero. +func (w *InstanceDeregisteredWaiter) Wait(ctx context.Context, params *DescribeInstanceHealthInput, maxWaitDur time.Duration, optFns ...func(*InstanceDeregisteredWaiterOptions)) error { + _, err := w.WaitForOutput(ctx, params, maxWaitDur, optFns...) + return err +} + +// WaitForOutput calls the waiter function for InstanceDeregistered waiter and +// returns the output of the successful operation. The maxWaitDur is the maximum +// wait duration the waiter will wait. The maxWaitDur is required and must be +// greater than zero. +func (w *InstanceDeregisteredWaiter) WaitForOutput(ctx context.Context, params *DescribeInstanceHealthInput, maxWaitDur time.Duration, optFns ...func(*InstanceDeregisteredWaiterOptions)) (*DescribeInstanceHealthOutput, error) { + if maxWaitDur <= 0 { + return nil, fmt.Errorf("maximum wait time for waiter must be greater than zero") + } + + options := w.options + for _, fn := range optFns { + fn(&options) + } + + if options.MaxDelay <= 0 { + options.MaxDelay = 120 * time.Second + } + + if options.MinDelay > options.MaxDelay { + return nil, fmt.Errorf("minimum waiter delay %v must be lesser than or equal to maximum waiter delay of %v.", options.MinDelay, options.MaxDelay) + } + + ctx, cancelFn := context.WithTimeout(ctx, maxWaitDur) + defer cancelFn() + + logger := smithywaiter.Logger{} + remainingTime := maxWaitDur + + var attempt int64 + for { + + attempt++ + apiOptions := options.APIOptions + start := time.Now() + + if options.LogWaitAttempts { + logger.Attempt = attempt + apiOptions = append([]func(*middleware.Stack) error{}, options.APIOptions...) + apiOptions = append(apiOptions, logger.AddLogger) + } + + out, err := w.client.DescribeInstanceHealth(ctx, params, func(o *Options) { + baseOpts := []func(*Options){ + addIsWaiterUserAgent, + } + o.APIOptions = append(o.APIOptions, apiOptions...) + for _, opt := range baseOpts { + opt(o) + } + for _, opt := range options.ClientOptions { + opt(o) + } + }) + + retryable, err := options.Retryable(ctx, params, out, err) + if err != nil { + return nil, err + } + if !retryable { + return out, nil + } + + remainingTime -= time.Since(start) + if remainingTime < options.MinDelay || remainingTime <= 0 { + break + } + + // compute exponential backoff between waiter retries + delay, err := smithywaiter.ComputeDelay( + attempt, options.MinDelay, options.MaxDelay, remainingTime, + ) + if err != nil { + return nil, fmt.Errorf("error computing waiter delay, %w", err) + } + + remainingTime -= delay + // sleep for the delay amount before invoking a request + if err := smithytime.SleepWithContext(ctx, delay); err != nil { + return nil, fmt.Errorf("request cancelled while waiting, %w", err) + } + } + return nil, fmt.Errorf("exceeded max wait time for InstanceDeregistered waiter") +} + +func instanceDeregisteredStateRetryable(ctx context.Context, input *DescribeInstanceHealthInput, output *DescribeInstanceHealthOutput, err error) (bool, error) { + + if err == nil { + v1 := output.InstanceStates + var v2 []string + for _, v := range v1 { + v3 := v.State + if v3 != nil { + v2 = append(v2, *v3) + } + } + expectedValue := "OutOfService" + match := len(v2) > 0 + for _, v := range v2 { + if string(v) != expectedValue { + match = false + break + } + } + + if match { + return false, nil + } + } + + if err != nil { + var apiErr smithy.APIError + ok := errors.As(err, &apiErr) + if !ok { + return false, fmt.Errorf("expected err to be of type smithy.APIError, got %w", err) + } + + if "InvalidInstance" == apiErr.ErrorCode() { + return false, nil + } + } + + if err != nil { + return false, err + } + return true, nil +} + +// InstanceInServiceWaiterOptions are waiter options for InstanceInServiceWaiter +type InstanceInServiceWaiterOptions struct { + + // Set of options to modify how an operation is invoked. These apply to all + // operations invoked for this client. Use functional options on operation call to + // modify this list for per operation behavior. + // + // Passing options here is functionally equivalent to passing values to this + // config's ClientOptions field that extend the inner client's APIOptions directly. + APIOptions []func(*middleware.Stack) error + + // Functional options to be passed to all operations invoked by this client. + // + // Function values that modify the inner APIOptions are applied after the waiter + // config's own APIOptions modifiers. + ClientOptions []func(*Options) + + // MinDelay is the minimum amount of time to delay between retries. If unset, + // InstanceInServiceWaiter will use default minimum delay of 15 seconds. Note that + // MinDelay must resolve to a value lesser than or equal to the MaxDelay. + MinDelay time.Duration + + // MaxDelay is the maximum amount of time to delay between retries. If unset or + // set to zero, InstanceInServiceWaiter will use default max delay of 120 seconds. + // Note that MaxDelay must resolve to value greater than or equal to the MinDelay. + MaxDelay time.Duration + + // LogWaitAttempts is used to enable logging for waiter retry attempts + LogWaitAttempts bool + + // Retryable is function that can be used to override the service defined + // waiter-behavior based on operation output, or returned error. This function is + // used by the waiter to decide if a state is retryable or a terminal state. + // + // By default service-modeled logic will populate this option. This option can + // thus be used to define a custom waiter state with fall-back to service-modeled + // waiter state mutators.The function returns an error in case of a failure state. + // In case of retry state, this function returns a bool value of true and nil + // error, while in case of success it returns a bool value of false and nil error. + Retryable func(context.Context, *DescribeInstanceHealthInput, *DescribeInstanceHealthOutput, error) (bool, error) +} + +// InstanceInServiceWaiter defines the waiters for InstanceInService +type InstanceInServiceWaiter struct { + client DescribeInstanceHealthAPIClient + + options InstanceInServiceWaiterOptions +} + +// NewInstanceInServiceWaiter constructs a InstanceInServiceWaiter. +func NewInstanceInServiceWaiter(client DescribeInstanceHealthAPIClient, optFns ...func(*InstanceInServiceWaiterOptions)) *InstanceInServiceWaiter { + options := InstanceInServiceWaiterOptions{} + options.MinDelay = 15 * time.Second + options.MaxDelay = 120 * time.Second + options.Retryable = instanceInServiceStateRetryable + + for _, fn := range optFns { + fn(&options) + } + return &InstanceInServiceWaiter{ + client: client, + options: options, + } +} + +// Wait calls the waiter function for InstanceInService waiter. The maxWaitDur is +// the maximum wait duration the waiter will wait. The maxWaitDur is required and +// must be greater than zero. +func (w *InstanceInServiceWaiter) Wait(ctx context.Context, params *DescribeInstanceHealthInput, maxWaitDur time.Duration, optFns ...func(*InstanceInServiceWaiterOptions)) error { + _, err := w.WaitForOutput(ctx, params, maxWaitDur, optFns...) + return err +} + +// WaitForOutput calls the waiter function for InstanceInService waiter and +// returns the output of the successful operation. The maxWaitDur is the maximum +// wait duration the waiter will wait. The maxWaitDur is required and must be +// greater than zero. +func (w *InstanceInServiceWaiter) WaitForOutput(ctx context.Context, params *DescribeInstanceHealthInput, maxWaitDur time.Duration, optFns ...func(*InstanceInServiceWaiterOptions)) (*DescribeInstanceHealthOutput, error) { + if maxWaitDur <= 0 { + return nil, fmt.Errorf("maximum wait time for waiter must be greater than zero") + } + + options := w.options + for _, fn := range optFns { + fn(&options) + } + + if options.MaxDelay <= 0 { + options.MaxDelay = 120 * time.Second + } + + if options.MinDelay > options.MaxDelay { + return nil, fmt.Errorf("minimum waiter delay %v must be lesser than or equal to maximum waiter delay of %v.", options.MinDelay, options.MaxDelay) + } + + ctx, cancelFn := context.WithTimeout(ctx, maxWaitDur) + defer cancelFn() + + logger := smithywaiter.Logger{} + remainingTime := maxWaitDur + + var attempt int64 + for { + + attempt++ + apiOptions := options.APIOptions + start := time.Now() + + if options.LogWaitAttempts { + logger.Attempt = attempt + apiOptions = append([]func(*middleware.Stack) error{}, options.APIOptions...) + apiOptions = append(apiOptions, logger.AddLogger) + } + + out, err := w.client.DescribeInstanceHealth(ctx, params, func(o *Options) { + baseOpts := []func(*Options){ + addIsWaiterUserAgent, + } + o.APIOptions = append(o.APIOptions, apiOptions...) + for _, opt := range baseOpts { + opt(o) + } + for _, opt := range options.ClientOptions { + opt(o) + } + }) + + retryable, err := options.Retryable(ctx, params, out, err) + if err != nil { + return nil, err + } + if !retryable { + return out, nil + } + + remainingTime -= time.Since(start) + if remainingTime < options.MinDelay || remainingTime <= 0 { + break + } + + // compute exponential backoff between waiter retries + delay, err := smithywaiter.ComputeDelay( + attempt, options.MinDelay, options.MaxDelay, remainingTime, + ) + if err != nil { + return nil, fmt.Errorf("error computing waiter delay, %w", err) + } + + remainingTime -= delay + // sleep for the delay amount before invoking a request + if err := smithytime.SleepWithContext(ctx, delay); err != nil { + return nil, fmt.Errorf("request cancelled while waiting, %w", err) + } + } + return nil, fmt.Errorf("exceeded max wait time for InstanceInService waiter") +} + +func instanceInServiceStateRetryable(ctx context.Context, input *DescribeInstanceHealthInput, output *DescribeInstanceHealthOutput, err error) (bool, error) { + + if err == nil { + v1 := output.InstanceStates + var v2 []string + for _, v := range v1 { + v3 := v.State + if v3 != nil { + v2 = append(v2, *v3) + } + } + expectedValue := "InService" + match := len(v2) > 0 + for _, v := range v2 { + if string(v) != expectedValue { + match = false + break + } + } + + if match { + return false, nil + } + } + + if err != nil { + var apiErr smithy.APIError + ok := errors.As(err, &apiErr) + if !ok { + return false, fmt.Errorf("expected err to be of type smithy.APIError, got %w", err) + } + + if "InvalidInstance" == apiErr.ErrorCode() { + return true, nil + } + } + + if err != nil { + return false, err + } + return true, nil +} + +// DescribeInstanceHealthAPIClient is a client that implements the +// DescribeInstanceHealth operation. +type DescribeInstanceHealthAPIClient interface { + DescribeInstanceHealth(context.Context, *DescribeInstanceHealthInput, ...func(*Options)) (*DescribeInstanceHealthOutput, error) +} + +var _ DescribeInstanceHealthAPIClient = (*Client)(nil) diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerAttributes.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerAttributes.go new file mode 100644 index 000000000..62932aa76 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerAttributes.go @@ -0,0 +1,94 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Describes the attributes for the specified load balancer. +func (c *Client) DescribeLoadBalancerAttributes(ctx context.Context, params *DescribeLoadBalancerAttributesInput, optFns ...func(*Options)) (*DescribeLoadBalancerAttributesOutput, error) { + if params == nil { + params = &DescribeLoadBalancerAttributesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeLoadBalancerAttributes", params, optFns, c.addOperationDescribeLoadBalancerAttributesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeLoadBalancerAttributesOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DescribeLoadBalancerAttributes. +type DescribeLoadBalancerAttributesInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of DescribeLoadBalancerAttributes. +type DescribeLoadBalancerAttributesOutput struct { + + // Information about the load balancer attributes. + LoadBalancerAttributes *types.LoadBalancerAttributes + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeLoadBalancerAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeLoadBalancerAttributes{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeLoadBalancerAttributes{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeLoadBalancerAttributesValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicies.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicies.go new file mode 100644 index 000000000..abf271af9 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicies.go @@ -0,0 +1,99 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Describes the specified policies. +// +// If you specify a load balancer name, the action returns the descriptions of all +// policies created for the load balancer. If you specify a policy name associated +// with your load balancer, the action returns the description of that policy. If +// you don't specify a load balancer name, the action returns descriptions of the +// specified sample policies, or descriptions of all sample policies. The names of +// the sample policies have the ELBSample- prefix. +func (c *Client) DescribeLoadBalancerPolicies(ctx context.Context, params *DescribeLoadBalancerPoliciesInput, optFns ...func(*Options)) (*DescribeLoadBalancerPoliciesOutput, error) { + if params == nil { + params = &DescribeLoadBalancerPoliciesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeLoadBalancerPolicies", params, optFns, c.addOperationDescribeLoadBalancerPoliciesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeLoadBalancerPoliciesOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DescribeLoadBalancerPolicies. +type DescribeLoadBalancerPoliciesInput struct { + + // The name of the load balancer. + LoadBalancerName *string + + // The names of the policies. + PolicyNames []string + + noSmithyDocumentSerde +} + +// Contains the output of DescribeLoadBalancerPolicies. +type DescribeLoadBalancerPoliciesOutput struct { + + // Information about the policies. + PolicyDescriptions []types.PolicyDescription + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeLoadBalancerPoliciesMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeLoadBalancerPolicies{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeLoadBalancerPolicies{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicyTypes.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicyTypes.go new file mode 100644 index 000000000..8ddcccb8e --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancerPolicyTypes.go @@ -0,0 +1,99 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Describes the specified load balancer policy types or all load balancer policy +// types. +// +// The description of each type indicates how it can be used. For example, some +// policies can be used only with layer 7 listeners, some policies can be used only +// with layer 4 listeners, and some policies can be used only with your EC2 +// instances. +// +// You can use CreateLoadBalancerPolicy to create a policy configuration for any of these policy types. +// Then, depending on the policy type, use either SetLoadBalancerPoliciesOfListeneror SetLoadBalancerPoliciesForBackendServer to set the policy. +func (c *Client) DescribeLoadBalancerPolicyTypes(ctx context.Context, params *DescribeLoadBalancerPolicyTypesInput, optFns ...func(*Options)) (*DescribeLoadBalancerPolicyTypesOutput, error) { + if params == nil { + params = &DescribeLoadBalancerPolicyTypesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeLoadBalancerPolicyTypes", params, optFns, c.addOperationDescribeLoadBalancerPolicyTypesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeLoadBalancerPolicyTypesOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DescribeLoadBalancerPolicyTypes. +type DescribeLoadBalancerPolicyTypesInput struct { + + // The names of the policy types. If no names are specified, describes all policy + // types defined by Elastic Load Balancing. + PolicyTypeNames []string + + noSmithyDocumentSerde +} + +// Contains the output of DescribeLoadBalancerPolicyTypes. +type DescribeLoadBalancerPolicyTypesOutput struct { + + // Information about the policy types. + PolicyTypeDescriptions []types.PolicyTypeDescription + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeLoadBalancerPolicyTypesMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeLoadBalancerPolicyTypes{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeLoadBalancerPolicyTypes{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancers.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancers.go new file mode 100644 index 000000000..4a9f1b331 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeLoadBalancers.go @@ -0,0 +1,185 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Describes the specified the load balancers. If no load balancers are specified, +// the call describes all of your load balancers. +func (c *Client) DescribeLoadBalancers(ctx context.Context, params *DescribeLoadBalancersInput, optFns ...func(*Options)) (*DescribeLoadBalancersOutput, error) { + if params == nil { + params = &DescribeLoadBalancersInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeLoadBalancers", params, optFns, c.addOperationDescribeLoadBalancersMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeLoadBalancersOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DescribeLoadBalancers. +type DescribeLoadBalancersInput struct { + + // The names of the load balancers. + LoadBalancerNames []string + + // The marker for the next set of results. (You received this marker from a + // previous call.) + Marker *string + + // The maximum number of results to return with this call (a number from 1 to + // 400). The default is 400. + PageSize *int32 + + noSmithyDocumentSerde +} + +// Contains the parameters for DescribeLoadBalancers. +type DescribeLoadBalancersOutput struct { + + // Information about the load balancers. + LoadBalancerDescriptions []types.LoadBalancerDescription + + // The marker to use when requesting the next set of results. If there are no + // additional results, the string is empty. + NextMarker *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeLoadBalancersMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeLoadBalancers{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeLoadBalancers{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// DescribeLoadBalancersPaginatorOptions is the paginator options for +// DescribeLoadBalancers +type DescribeLoadBalancersPaginatorOptions struct { + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// DescribeLoadBalancersPaginator is a paginator for DescribeLoadBalancers +type DescribeLoadBalancersPaginator struct { + options DescribeLoadBalancersPaginatorOptions + client DescribeLoadBalancersAPIClient + params *DescribeLoadBalancersInput + nextToken *string + firstPage bool +} + +// NewDescribeLoadBalancersPaginator returns a new DescribeLoadBalancersPaginator +func NewDescribeLoadBalancersPaginator(client DescribeLoadBalancersAPIClient, params *DescribeLoadBalancersInput, optFns ...func(*DescribeLoadBalancersPaginatorOptions)) *DescribeLoadBalancersPaginator { + if params == nil { + params = &DescribeLoadBalancersInput{} + } + + options := DescribeLoadBalancersPaginatorOptions{} + + for _, fn := range optFns { + fn(&options) + } + + return &DescribeLoadBalancersPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.Marker, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *DescribeLoadBalancersPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next DescribeLoadBalancers page. +func (p *DescribeLoadBalancersPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*DescribeLoadBalancersOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.Marker = p.nextToken + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.DescribeLoadBalancers(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextMarker + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// DescribeLoadBalancersAPIClient is a client that implements the +// DescribeLoadBalancers operation. +type DescribeLoadBalancersAPIClient interface { + DescribeLoadBalancers(context.Context, *DescribeLoadBalancersInput, ...func(*Options)) (*DescribeLoadBalancersOutput, error) +} + +var _ DescribeLoadBalancersAPIClient = (*Client)(nil) diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeTags.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeTags.go new file mode 100644 index 000000000..24ffb3ca6 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DescribeTags.go @@ -0,0 +1,94 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Describes the tags associated with the specified load balancers. +func (c *Client) DescribeTags(ctx context.Context, params *DescribeTagsInput, optFns ...func(*Options)) (*DescribeTagsOutput, error) { + if params == nil { + params = &DescribeTagsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeTags", params, optFns, c.addOperationDescribeTagsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeTagsOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DescribeTags. +type DescribeTagsInput struct { + + // The names of the load balancers. + // + // This member is required. + LoadBalancerNames []string + + noSmithyDocumentSerde +} + +// Contains the output for DescribeTags. +type DescribeTagsOutput struct { + + // Information about the tags. + TagDescriptions []types.TagDescription + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeTagsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDescribeTags{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDescribeTags{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDescribeTagsValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DetachLoadBalancerFromSubnets.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DetachLoadBalancerFromSubnets.go new file mode 100644 index 000000000..f5a1f0bad --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DetachLoadBalancerFromSubnets.go @@ -0,0 +1,103 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Removes the specified subnets from the set of configured subnets for the load +// balancer. +// +// After a subnet is removed, all EC2 instances registered with the load balancer +// in the removed subnet go into the OutOfService state. Then, the load balancer +// balances the traffic among the remaining routable subnets. +func (c *Client) DetachLoadBalancerFromSubnets(ctx context.Context, params *DetachLoadBalancerFromSubnetsInput, optFns ...func(*Options)) (*DetachLoadBalancerFromSubnetsOutput, error) { + if params == nil { + params = &DetachLoadBalancerFromSubnetsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DetachLoadBalancerFromSubnets", params, optFns, c.addOperationDetachLoadBalancerFromSubnetsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DetachLoadBalancerFromSubnetsOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DetachLoadBalancerFromSubnets. +type DetachLoadBalancerFromSubnetsInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The IDs of the subnets. + // + // This member is required. + Subnets []string + + noSmithyDocumentSerde +} + +// Contains the output of DetachLoadBalancerFromSubnets. +type DetachLoadBalancerFromSubnetsOutput struct { + + // The IDs of the remaining subnets for the load balancer. + Subnets []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDetachLoadBalancerFromSubnetsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDetachLoadBalancerFromSubnets{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDetachLoadBalancerFromSubnets{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDetachLoadBalancerFromSubnetsValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DisableAvailabilityZonesForLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DisableAvailabilityZonesForLoadBalancer.go new file mode 100644 index 000000000..c9a6d586f --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_DisableAvailabilityZonesForLoadBalancer.go @@ -0,0 +1,111 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Removes the specified Availability Zones from the set of Availability Zones for +// the specified load balancer in EC2-Classic or a default VPC. +// +// For load balancers in a non-default VPC, use DetachLoadBalancerFromSubnets. +// +// There must be at least one Availability Zone registered with a load balancer at +// all times. After an Availability Zone is removed, all instances registered with +// the load balancer that are in the removed Availability Zone go into the +// OutOfService state. Then, the load balancer attempts to equally balance the +// traffic among its remaining Availability Zones. +// +// For more information, see [Add or Remove Availability Zones] in the Classic Load Balancers Guide. +// +// [Add or Remove Availability Zones]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-disable-az.html +func (c *Client) DisableAvailabilityZonesForLoadBalancer(ctx context.Context, params *DisableAvailabilityZonesForLoadBalancerInput, optFns ...func(*Options)) (*DisableAvailabilityZonesForLoadBalancerOutput, error) { + if params == nil { + params = &DisableAvailabilityZonesForLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DisableAvailabilityZonesForLoadBalancer", params, optFns, c.addOperationDisableAvailabilityZonesForLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DisableAvailabilityZonesForLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for DisableAvailabilityZonesForLoadBalancer. +type DisableAvailabilityZonesForLoadBalancerInput struct { + + // The Availability Zones. + // + // This member is required. + AvailabilityZones []string + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output for DisableAvailabilityZonesForLoadBalancer. +type DisableAvailabilityZonesForLoadBalancerOutput struct { + + // The remaining Availability Zones for the load balancer. + AvailabilityZones []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDisableAvailabilityZonesForLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpDisableAvailabilityZonesForLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpDisableAvailabilityZonesForLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpDisableAvailabilityZonesForLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_EnableAvailabilityZonesForLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_EnableAvailabilityZonesForLoadBalancer.go new file mode 100644 index 000000000..3afc61d8f --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_EnableAvailabilityZonesForLoadBalancer.go @@ -0,0 +1,107 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Adds the specified Availability Zones to the set of Availability Zones for the +// specified load balancer in EC2-Classic or a default VPC. +// +// For load balancers in a non-default VPC, use AttachLoadBalancerToSubnets. +// +// The load balancer evenly distributes requests across all its registered +// Availability Zones that contain instances. For more information, see [Add or Remove Availability Zones]in the +// Classic Load Balancers Guide. +// +// [Add or Remove Availability Zones]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-disable-az.html +func (c *Client) EnableAvailabilityZonesForLoadBalancer(ctx context.Context, params *EnableAvailabilityZonesForLoadBalancerInput, optFns ...func(*Options)) (*EnableAvailabilityZonesForLoadBalancerOutput, error) { + if params == nil { + params = &EnableAvailabilityZonesForLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "EnableAvailabilityZonesForLoadBalancer", params, optFns, c.addOperationEnableAvailabilityZonesForLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*EnableAvailabilityZonesForLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for EnableAvailabilityZonesForLoadBalancer. +type EnableAvailabilityZonesForLoadBalancerInput struct { + + // The Availability Zones. These must be in the same region as the load balancer. + // + // This member is required. + AvailabilityZones []string + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of EnableAvailabilityZonesForLoadBalancer. +type EnableAvailabilityZonesForLoadBalancerOutput struct { + + // The updated list of Availability Zones for the load balancer. + AvailabilityZones []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationEnableAvailabilityZonesForLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpEnableAvailabilityZonesForLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpEnableAvailabilityZonesForLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpEnableAvailabilityZonesForLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ModifyLoadBalancerAttributes.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ModifyLoadBalancerAttributes.go new file mode 100644 index 000000000..c4f558b04 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_ModifyLoadBalancerAttributes.go @@ -0,0 +1,122 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Modifies the attributes of the specified load balancer. +// +// You can modify the load balancer attributes, such as AccessLogs , +// ConnectionDraining , and CrossZoneLoadBalancing by either enabling or disabling +// them. Or, you can modify the load balancer attribute ConnectionSettings by +// specifying an idle connection timeout value for your load balancer. +// +// For more information, see the following in the Classic Load Balancers Guide: +// +// [Cross-Zone Load Balancing] +// +// [Connection Draining] +// +// [Access Logs] +// +// [Idle Connection Timeout] +// +// [Cross-Zone Load Balancing]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-disable-crosszone-lb.html +// [Idle Connection Timeout]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-idle-timeout.html +// [Access Logs]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/access-log-collection.html +// [Connection Draining]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-conn-drain.html +func (c *Client) ModifyLoadBalancerAttributes(ctx context.Context, params *ModifyLoadBalancerAttributesInput, optFns ...func(*Options)) (*ModifyLoadBalancerAttributesOutput, error) { + if params == nil { + params = &ModifyLoadBalancerAttributesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ModifyLoadBalancerAttributes", params, optFns, c.addOperationModifyLoadBalancerAttributesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ModifyLoadBalancerAttributesOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for ModifyLoadBalancerAttributes. +type ModifyLoadBalancerAttributesInput struct { + + // The attributes for the load balancer. + // + // This member is required. + LoadBalancerAttributes *types.LoadBalancerAttributes + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of ModifyLoadBalancerAttributes. +type ModifyLoadBalancerAttributesOutput struct { + + // Information about the load balancer attributes. + LoadBalancerAttributes *types.LoadBalancerAttributes + + // The name of the load balancer. + LoadBalancerName *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationModifyLoadBalancerAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpModifyLoadBalancerAttributes{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpModifyLoadBalancerAttributes{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpModifyLoadBalancerAttributesValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RegisterInstancesWithLoadBalancer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RegisterInstancesWithLoadBalancer.go new file mode 100644 index 000000000..c16e1cfa4 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RegisterInstancesWithLoadBalancer.go @@ -0,0 +1,121 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Adds the specified instances to the specified load balancer. +// +// The instance must be a running instance in the same network as the load +// balancer (EC2-Classic or the same VPC). If you have EC2-Classic instances and a +// load balancer in a VPC with ClassicLink enabled, you can link the EC2-Classic +// instances to that VPC and then register the linked EC2-Classic instances with +// the load balancer in the VPC. +// +// Note that RegisterInstanceWithLoadBalancer completes when the request has been +// registered. Instance registration takes a little time to complete. To check the +// state of the registered instances, use DescribeLoadBalancersor DescribeInstanceHealth. +// +// After the instance is registered, it starts receiving traffic and requests from +// the load balancer. Any instance that is not in one of the Availability Zones +// registered for the load balancer is moved to the OutOfService state. If an +// Availability Zone is added to the load balancer later, any instances registered +// with the load balancer move to the InService state. +// +// To deregister instances from a load balancer, use DeregisterInstancesFromLoadBalancer. +// +// For more information, see [Register or De-Register EC2 Instances] in the Classic Load Balancers Guide. +// +// [Register or De-Register EC2 Instances]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-deregister-register-instances.html +func (c *Client) RegisterInstancesWithLoadBalancer(ctx context.Context, params *RegisterInstancesWithLoadBalancerInput, optFns ...func(*Options)) (*RegisterInstancesWithLoadBalancerOutput, error) { + if params == nil { + params = &RegisterInstancesWithLoadBalancerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "RegisterInstancesWithLoadBalancer", params, optFns, c.addOperationRegisterInstancesWithLoadBalancerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*RegisterInstancesWithLoadBalancerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for RegisterInstancesWithLoadBalancer. +type RegisterInstancesWithLoadBalancerInput struct { + + // The IDs of the instances. + // + // This member is required. + Instances []types.Instance + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + noSmithyDocumentSerde +} + +// Contains the output of RegisterInstancesWithLoadBalancer. +type RegisterInstancesWithLoadBalancerOutput struct { + + // The updated list of instances for the load balancer. + Instances []types.Instance + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationRegisterInstancesWithLoadBalancerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpRegisterInstancesWithLoadBalancer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpRegisterInstancesWithLoadBalancer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpRegisterInstancesWithLoadBalancerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RemoveTags.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RemoveTags.go new file mode 100644 index 000000000..51278afb3 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_RemoveTags.go @@ -0,0 +1,96 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + "github.com/aws/smithy-go/middleware" +) + +// Removes one or more tags from the specified load balancer. +func (c *Client) RemoveTags(ctx context.Context, params *RemoveTagsInput, optFns ...func(*Options)) (*RemoveTagsOutput, error) { + if params == nil { + params = &RemoveTagsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "RemoveTags", params, optFns, c.addOperationRemoveTagsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*RemoveTagsOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for RemoveTags. +type RemoveTagsInput struct { + + // The name of the load balancer. You can specify a maximum of one load balancer + // name. + // + // This member is required. + LoadBalancerNames []string + + // The list of tag keys to remove. + // + // This member is required. + Tags []types.TagKeyOnly + + noSmithyDocumentSerde +} + +// Contains the output of RemoveTags. +type RemoveTagsOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationRemoveTagsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpRemoveTags{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpRemoveTags{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpRemoveTagsValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerListenerSSLCertificate.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerListenerSSLCertificate.go new file mode 100644 index 000000000..3d820c389 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerListenerSSLCertificate.go @@ -0,0 +1,106 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Sets the certificate that terminates the specified listener's SSL connections. +// The specified certificate replaces any prior certificate that was used on the +// same load balancer and port. +// +// For more information about updating your SSL certificate, see [Replace the SSL Certificate for Your Load Balancer] in the Classic +// Load Balancers Guide. +// +// [Replace the SSL Certificate for Your Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-update-ssl-cert.html +func (c *Client) SetLoadBalancerListenerSSLCertificate(ctx context.Context, params *SetLoadBalancerListenerSSLCertificateInput, optFns ...func(*Options)) (*SetLoadBalancerListenerSSLCertificateOutput, error) { + if params == nil { + params = &SetLoadBalancerListenerSSLCertificateInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "SetLoadBalancerListenerSSLCertificate", params, optFns, c.addOperationSetLoadBalancerListenerSSLCertificateMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*SetLoadBalancerListenerSSLCertificateOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for SetLoadBalancerListenerSSLCertificate. +type SetLoadBalancerListenerSSLCertificateInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The port that uses the specified SSL certificate. + // + // This member is required. + LoadBalancerPort int32 + + // The Amazon Resource Name (ARN) of the SSL certificate. + // + // This member is required. + SSLCertificateId *string + + noSmithyDocumentSerde +} + +// Contains the output of SetLoadBalancerListenerSSLCertificate. +type SetLoadBalancerListenerSSLCertificateOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationSetLoadBalancerListenerSSLCertificateMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpSetLoadBalancerListenerSSLCertificate{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpSetLoadBalancerListenerSSLCertificate{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpSetLoadBalancerListenerSSLCertificateValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesForBackendServer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesForBackendServer.go new file mode 100644 index 000000000..b38b1fc3e --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesForBackendServer.go @@ -0,0 +1,116 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Replaces the set of policies associated with the specified port on which the +// EC2 instance is listening with a new set of policies. At this time, only the +// back-end server authentication policy type can be applied to the instance ports; +// this policy type is composed of multiple public key policies. +// +// Each time you use SetLoadBalancerPoliciesForBackendServer to enable the +// policies, use the PolicyNames parameter to list the policies that you want to +// enable. +// +// You can use DescribeLoadBalancers or DescribeLoadBalancerPolicies to verify that the policy is associated with the EC2 instance. +// +// For more information about enabling back-end instance authentication, see [Configure Back-end Instance Authentication] in +// the Classic Load Balancers Guide. For more information about Proxy Protocol, see +// [Configure Proxy Protocol Support]in the Classic Load Balancers Guide. +// +// [Configure Back-end Instance Authentication]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html#configure_backendauth_clt +// [Configure Proxy Protocol Support]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-proxy-protocol.html +func (c *Client) SetLoadBalancerPoliciesForBackendServer(ctx context.Context, params *SetLoadBalancerPoliciesForBackendServerInput, optFns ...func(*Options)) (*SetLoadBalancerPoliciesForBackendServerOutput, error) { + if params == nil { + params = &SetLoadBalancerPoliciesForBackendServerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "SetLoadBalancerPoliciesForBackendServer", params, optFns, c.addOperationSetLoadBalancerPoliciesForBackendServerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*SetLoadBalancerPoliciesForBackendServerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for SetLoadBalancerPoliciesForBackendServer. +type SetLoadBalancerPoliciesForBackendServerInput struct { + + // The port number associated with the EC2 instance. + // + // This member is required. + InstancePort *int32 + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The names of the policies. If the list is empty, then all current polices are + // removed from the EC2 instance. + // + // This member is required. + PolicyNames []string + + noSmithyDocumentSerde +} + +// Contains the output of SetLoadBalancerPoliciesForBackendServer. +type SetLoadBalancerPoliciesForBackendServerOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationSetLoadBalancerPoliciesForBackendServerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpSetLoadBalancerPoliciesForBackendServer{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpSetLoadBalancerPoliciesForBackendServer{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpSetLoadBalancerPoliciesForBackendServerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesOfListener.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesOfListener.go new file mode 100644 index 000000000..8f7fab6c9 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/api_op_SetLoadBalancerPoliciesOfListener.go @@ -0,0 +1,111 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/smithy-go/middleware" +) + +// Replaces the current set of policies for the specified load balancer port with +// the specified set of policies. +// +// To enable back-end server authentication, use SetLoadBalancerPoliciesForBackendServer. +// +// For more information about setting policies, see [Update the SSL Negotiation Configuration], [Duration-Based Session Stickiness], and [Application-Controlled Session Stickiness] in the Classic Load +// Balancers Guide. +// +// [Update the SSL Negotiation Configuration]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/ssl-config-update.html +// [Duration-Based Session Stickiness]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html#enable-sticky-sessions-duration +// [Application-Controlled Session Stickiness]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html#enable-sticky-sessions-application +func (c *Client) SetLoadBalancerPoliciesOfListener(ctx context.Context, params *SetLoadBalancerPoliciesOfListenerInput, optFns ...func(*Options)) (*SetLoadBalancerPoliciesOfListenerOutput, error) { + if params == nil { + params = &SetLoadBalancerPoliciesOfListenerInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "SetLoadBalancerPoliciesOfListener", params, optFns, c.addOperationSetLoadBalancerPoliciesOfListenerMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*SetLoadBalancerPoliciesOfListenerOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Contains the parameters for SetLoadBalancePoliciesOfListener. +type SetLoadBalancerPoliciesOfListenerInput struct { + + // The name of the load balancer. + // + // This member is required. + LoadBalancerName *string + + // The external port of the load balancer. + // + // This member is required. + LoadBalancerPort int32 + + // The names of the policies. This list must include all policies to be enabled. + // If you omit a policy that is currently enabled, it is disabled. If the list is + // empty, all current policies are disabled. + // + // This member is required. + PolicyNames []string + + noSmithyDocumentSerde +} + +// Contains the output of SetLoadBalancePoliciesOfListener. +type SetLoadBalancerPoliciesOfListenerOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationSetLoadBalancerPoliciesOfListenerMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsquery_serializeOpSetLoadBalancerPoliciesOfListener{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsquery_deserializeOpSetLoadBalancerPoliciesOfListener{}, middleware.After) + if err != nil { + return err + } + + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpSetLoadBalancerPoliciesOfListenerValidationMiddleware(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/auth.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/auth.go new file mode 100644 index 000000000..db68b711e --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/auth.go @@ -0,0 +1,355 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + smithy "github.com/aws/smithy-go" + smithyauth "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/metrics" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "slices" + "strings" +) + +func bindAuthParamsRegion(_ interface{}, params *AuthResolverParameters, _ interface{}, options Options) error { + params.Region = options.Region + return nil +} + +type setLegacyContextSigningOptionsMiddleware struct { +} + +func (*setLegacyContextSigningOptionsMiddleware) ID() string { + return "setLegacyContextSigningOptions" +} + +func (m *setLegacyContextSigningOptionsMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + rscheme := getResolvedAuthScheme(ctx) + schemeID := rscheme.Scheme.SchemeID() + + if sn := awsmiddleware.GetSigningName(ctx); sn != "" { + if schemeID == "aws.auth#sigv4" { + smithyhttp.SetSigV4SigningName(&rscheme.SignerProperties, sn) + } else if schemeID == "aws.auth#sigv4a" { + smithyhttp.SetSigV4ASigningName(&rscheme.SignerProperties, sn) + } + } + + if sr := awsmiddleware.GetSigningRegion(ctx); sr != "" { + if schemeID == "aws.auth#sigv4" { + smithyhttp.SetSigV4SigningRegion(&rscheme.SignerProperties, sr) + } else if schemeID == "aws.auth#sigv4a" { + smithyhttp.SetSigV4ASigningRegions(&rscheme.SignerProperties, []string{sr}) + } + } + + return next.HandleFinalize(ctx, in) +} + +func addSetLegacyContextSigningOptionsMiddleware(stack *middleware.Stack) error { + return stack.Finalize.Insert(&setLegacyContextSigningOptionsMiddleware{}, "Signing", middleware.Before) +} + +type withAnonymous struct { + resolver AuthSchemeResolver +} + +var _ AuthSchemeResolver = (*withAnonymous)(nil) + +func (v *withAnonymous) ResolveAuthSchemes(ctx context.Context, params *AuthResolverParameters) ([]*smithyauth.Option, error) { + opts, err := v.resolver.ResolveAuthSchemes(ctx, params) + if err != nil { + return nil, err + } + + opts = append(opts, &smithyauth.Option{ + SchemeID: smithyauth.SchemeIDAnonymous, + }) + return opts, nil +} + +func wrapWithAnonymousAuth(options *Options) { + if _, ok := options.AuthSchemeResolver.(*defaultAuthSchemeResolver); !ok { + return + } + + options.AuthSchemeResolver = &withAnonymous{ + resolver: options.AuthSchemeResolver, + } +} + +// AuthResolverParameters contains the set of inputs necessary for auth scheme +// resolution. +type AuthResolverParameters struct { + // The name of the operation being invoked. + Operation string + + // The region in which the operation is being invoked. + Region string +} + +func bindAuthResolverParams(ctx context.Context, operation string, input interface{}, options Options) (*AuthResolverParameters, error) { + params := &AuthResolverParameters{ + Operation: operation, + } + + if err := bindAuthParamsRegion(ctx, params, input, options); err != nil { + return nil, err + } + + return params, nil +} + +// AuthSchemeResolver returns a set of possible authentication options for an +// operation. +type AuthSchemeResolver interface { + ResolveAuthSchemes(context.Context, *AuthResolverParameters) ([]*smithyauth.Option, error) +} + +type defaultAuthSchemeResolver struct{} + +var _ AuthSchemeResolver = (*defaultAuthSchemeResolver)(nil) + +func (*defaultAuthSchemeResolver) ResolveAuthSchemes(ctx context.Context, params *AuthResolverParameters) ([]*smithyauth.Option, error) { + if overrides, ok := operationAuthOptions[params.Operation]; ok { + return overrides(params), nil + } + return serviceAuthOptions(params), nil +} + +var operationAuthOptions = map[string]func(*AuthResolverParameters) []*smithyauth.Option{} + +func serviceAuthOptions(params *AuthResolverParameters) []*smithyauth.Option { + return []*smithyauth.Option{ + { + SchemeID: smithyauth.SchemeIDSigV4, + SignerProperties: func() smithy.Properties { + var props smithy.Properties + smithyhttp.SetSigV4SigningName(&props, "elasticloadbalancing") + smithyhttp.SetSigV4SigningRegion(&props, params.Region) + return props + }(), + }, + } +} + +type resolveAuthSchemeMiddleware struct { + operation string + options Options +} + +func (*resolveAuthSchemeMiddleware) ID() string { + return "ResolveAuthScheme" +} + +func (m *resolveAuthSchemeMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "ResolveAuthScheme") + defer span.End() + + params, err := bindAuthResolverParams(ctx, m.operation, getOperationInput(ctx), m.options) + if err != nil { + return out, metadata, fmt.Errorf("bind auth scheme params: %w", err) + } + options, err := m.options.AuthSchemeResolver.ResolveAuthSchemes(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("resolve auth scheme: %w", err) + } + + scheme, ok := m.selectScheme(options) + if !ok { + return out, metadata, fmt.Errorf("could not select an auth scheme") + } + + ctx = setResolvedAuthScheme(ctx, scheme) + + span.SetProperty("auth.scheme_id", scheme.Scheme.SchemeID()) + span.End() + return next.HandleFinalize(ctx, in) +} + +func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) (*resolvedAuthScheme, bool) { + sorted := sortAuthOptions(options, m.options.AuthSchemePreference) + for _, option := range sorted { + if option.SchemeID == smithyauth.SchemeIDAnonymous { + return newResolvedAuthScheme(smithyhttp.NewAnonymousScheme(), option), true + } + + for _, scheme := range m.options.AuthSchemes { + if !matchSchemeID(scheme.SchemeID(), option.SchemeID) { + continue + } + + if scheme.IdentityResolver(m.options) != nil { + return newResolvedAuthScheme(scheme, option), true + } + } + } + + return nil, false +} + +func matchSchemeID(registered, option string) bool { + if registered == option { + return true + } + if i := strings.LastIndex(registered, "#"); i != -1 { + return registered[i+1:] == option + } + return false +} + +func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { + byPriority := make([]*smithyauth.Option, 0, len(options)) + for _, prefName := range preferred { + for _, option := range options { + optName := option.SchemeID + if parts := strings.Split(option.SchemeID, "#"); len(parts) == 2 { + optName = parts[1] + } + if prefName == optName { + byPriority = append(byPriority, option) + } + } + } + for _, option := range options { + if !slices.ContainsFunc(byPriority, func(o *smithyauth.Option) bool { + return o.SchemeID == option.SchemeID + }) { + byPriority = append(byPriority, option) + } + } + return byPriority +} + +type resolvedAuthSchemeKey struct{} + +type resolvedAuthScheme struct { + Scheme smithyhttp.AuthScheme + IdentityProperties smithy.Properties + SignerProperties smithy.Properties +} + +func newResolvedAuthScheme(scheme smithyhttp.AuthScheme, option *smithyauth.Option) *resolvedAuthScheme { + return &resolvedAuthScheme{ + Scheme: scheme, + IdentityProperties: option.IdentityProperties, + SignerProperties: option.SignerProperties, + } +} + +func setResolvedAuthScheme(ctx context.Context, scheme *resolvedAuthScheme) context.Context { + return middleware.WithStackValue(ctx, resolvedAuthSchemeKey{}, scheme) +} + +func getResolvedAuthScheme(ctx context.Context) *resolvedAuthScheme { + v, _ := middleware.GetStackValue(ctx, resolvedAuthSchemeKey{}).(*resolvedAuthScheme) + return v +} + +type getIdentityMiddleware struct { + options Options +} + +func (*getIdentityMiddleware) ID() string { + return "GetIdentity" +} + +func (m *getIdentityMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + innerCtx, span := tracing.StartSpan(ctx, "GetIdentity") + defer span.End() + + rscheme := getResolvedAuthScheme(innerCtx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + resolver := rscheme.Scheme.IdentityResolver(m.options) + if resolver == nil { + return out, metadata, fmt.Errorf("no identity resolver") + } + + identity, err := timeOperationMetric(ctx, "client.call.resolve_identity_duration", + func() (smithyauth.Identity, error) { + return resolver.GetIdentity(innerCtx, rscheme.IdentityProperties) + }, + func(o *metrics.RecordMetricOptions) { + o.Properties.Set("auth.scheme_id", rscheme.Scheme.SchemeID()) + }) + if err != nil { + return out, metadata, fmt.Errorf("get identity: %w", err) + } + + ctx = setIdentity(ctx, identity) + + span.End() + return next.HandleFinalize(ctx, in) +} + +type identityKey struct{} + +func setIdentity(ctx context.Context, identity smithyauth.Identity) context.Context { + return middleware.WithStackValue(ctx, identityKey{}, identity) +} + +func getIdentity(ctx context.Context) smithyauth.Identity { + v, _ := middleware.GetStackValue(ctx, identityKey{}).(smithyauth.Identity) + return v +} + +type signRequestMiddleware struct { + options Options +} + +func (*signRequestMiddleware) ID() string { + return "Signing" +} + +func (m *signRequestMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "SignRequest") + defer span.End() + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unexpected transport type %T", in.Request) + } + + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + identity := getIdentity(ctx) + if identity == nil { + return out, metadata, fmt.Errorf("no identity") + } + + signer := rscheme.Scheme.Signer() + if signer == nil { + return out, metadata, fmt.Errorf("no signer") + } + + _, err = timeOperationMetric(ctx, "client.call.signing_duration", func() (any, error) { + return nil, signer.SignRequest(ctx, req, identity, rscheme.SignerProperties) + }, func(o *metrics.RecordMetricOptions) { + o.Properties.Set("auth.scheme_id", rscheme.Scheme.SchemeID()) + }) + if err != nil { + return out, metadata, fmt.Errorf("sign request: %w", err) + } + + span.End() + return next.HandleFinalize(ctx, in) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/deserializers.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/deserializers.go new file mode 100644 index 000000000..1521cca50 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/deserializers.go @@ -0,0 +1,9861 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "bytes" + "context" + "encoding/xml" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + awsxml "github.com/aws/aws-sdk-go-v2/aws/protocol/xml" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + smithy "github.com/aws/smithy-go" + smithyxml "github.com/aws/smithy-go/encoding/xml" + smithyio "github.com/aws/smithy-go/io" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "io" + "strconv" + "strings" +) + +type awsAwsquery_deserializeOpAddTags struct { +} + +func (*awsAwsquery_deserializeOpAddTags) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpAddTags) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorAddTags(response, &metadata) + } + output := &AddTagsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("AddTagsResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentAddTagsOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorAddTags(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("DuplicateTagKeys", errorCode): + return awsAwsquery_deserializeErrorDuplicateTagKeysException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyTags", errorCode): + return awsAwsquery_deserializeErrorTooManyTagsException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpApplySecurityGroupsToLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpApplySecurityGroupsToLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpApplySecurityGroupsToLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorApplySecurityGroupsToLoadBalancer(response, &metadata) + } + output := &ApplySecurityGroupsToLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("ApplySecurityGroupsToLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentApplySecurityGroupsToLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorApplySecurityGroupsToLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("InvalidSecurityGroup", errorCode): + return awsAwsquery_deserializeErrorInvalidSecurityGroupException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpAttachLoadBalancerToSubnets struct { +} + +func (*awsAwsquery_deserializeOpAttachLoadBalancerToSubnets) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpAttachLoadBalancerToSubnets) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorAttachLoadBalancerToSubnets(response, &metadata) + } + output := &AttachLoadBalancerToSubnetsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("AttachLoadBalancerToSubnetsResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentAttachLoadBalancerToSubnetsOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorAttachLoadBalancerToSubnets(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("InvalidSubnet", errorCode): + return awsAwsquery_deserializeErrorInvalidSubnetException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("SubnetNotFound", errorCode): + return awsAwsquery_deserializeErrorSubnetNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpConfigureHealthCheck struct { +} + +func (*awsAwsquery_deserializeOpConfigureHealthCheck) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpConfigureHealthCheck) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorConfigureHealthCheck(response, &metadata) + } + output := &ConfigureHealthCheckOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("ConfigureHealthCheckResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentConfigureHealthCheckOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorConfigureHealthCheck(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpCreateAppCookieStickinessPolicy struct { +} + +func (*awsAwsquery_deserializeOpCreateAppCookieStickinessPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpCreateAppCookieStickinessPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorCreateAppCookieStickinessPolicy(response, &metadata) + } + output := &CreateAppCookieStickinessPolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("CreateAppCookieStickinessPolicyResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentCreateAppCookieStickinessPolicyOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorCreateAppCookieStickinessPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("DuplicatePolicyName", errorCode): + return awsAwsquery_deserializeErrorDuplicatePolicyNameException(response, errorBody) + + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyPolicies", errorCode): + return awsAwsquery_deserializeErrorTooManyPoliciesException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpCreateLBCookieStickinessPolicy struct { +} + +func (*awsAwsquery_deserializeOpCreateLBCookieStickinessPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpCreateLBCookieStickinessPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorCreateLBCookieStickinessPolicy(response, &metadata) + } + output := &CreateLBCookieStickinessPolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("CreateLBCookieStickinessPolicyResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentCreateLBCookieStickinessPolicyOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorCreateLBCookieStickinessPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("DuplicatePolicyName", errorCode): + return awsAwsquery_deserializeErrorDuplicatePolicyNameException(response, errorBody) + + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyPolicies", errorCode): + return awsAwsquery_deserializeErrorTooManyPoliciesException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpCreateLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpCreateLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpCreateLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorCreateLoadBalancer(response, &metadata) + } + output := &CreateLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("CreateLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentCreateLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorCreateLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("CertificateNotFound", errorCode): + return awsAwsquery_deserializeErrorCertificateNotFoundException(response, errorBody) + + case strings.EqualFold("DuplicateLoadBalancerName", errorCode): + return awsAwsquery_deserializeErrorDuplicateAccessPointNameException(response, errorBody) + + case strings.EqualFold("DuplicateTagKeys", errorCode): + return awsAwsquery_deserializeErrorDuplicateTagKeysException(response, errorBody) + + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("InvalidScheme", errorCode): + return awsAwsquery_deserializeErrorInvalidSchemeException(response, errorBody) + + case strings.EqualFold("InvalidSecurityGroup", errorCode): + return awsAwsquery_deserializeErrorInvalidSecurityGroupException(response, errorBody) + + case strings.EqualFold("InvalidSubnet", errorCode): + return awsAwsquery_deserializeErrorInvalidSubnetException(response, errorBody) + + case strings.EqualFold("OperationNotPermitted", errorCode): + return awsAwsquery_deserializeErrorOperationNotPermittedException(response, errorBody) + + case strings.EqualFold("SubnetNotFound", errorCode): + return awsAwsquery_deserializeErrorSubnetNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyLoadBalancers", errorCode): + return awsAwsquery_deserializeErrorTooManyAccessPointsException(response, errorBody) + + case strings.EqualFold("TooManyTags", errorCode): + return awsAwsquery_deserializeErrorTooManyTagsException(response, errorBody) + + case strings.EqualFold("UnsupportedProtocol", errorCode): + return awsAwsquery_deserializeErrorUnsupportedProtocolException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpCreateLoadBalancerListeners struct { +} + +func (*awsAwsquery_deserializeOpCreateLoadBalancerListeners) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpCreateLoadBalancerListeners) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorCreateLoadBalancerListeners(response, &metadata) + } + output := &CreateLoadBalancerListenersOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("CreateLoadBalancerListenersResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentCreateLoadBalancerListenersOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorCreateLoadBalancerListeners(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("CertificateNotFound", errorCode): + return awsAwsquery_deserializeErrorCertificateNotFoundException(response, errorBody) + + case strings.EqualFold("DuplicateListener", errorCode): + return awsAwsquery_deserializeErrorDuplicateListenerException(response, errorBody) + + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("UnsupportedProtocol", errorCode): + return awsAwsquery_deserializeErrorUnsupportedProtocolException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpCreateLoadBalancerPolicy struct { +} + +func (*awsAwsquery_deserializeOpCreateLoadBalancerPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpCreateLoadBalancerPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorCreateLoadBalancerPolicy(response, &metadata) + } + output := &CreateLoadBalancerPolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("CreateLoadBalancerPolicyResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentCreateLoadBalancerPolicyOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorCreateLoadBalancerPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("DuplicatePolicyName", errorCode): + return awsAwsquery_deserializeErrorDuplicatePolicyNameException(response, errorBody) + + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("PolicyTypeNotFound", errorCode): + return awsAwsquery_deserializeErrorPolicyTypeNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyPolicies", errorCode): + return awsAwsquery_deserializeErrorTooManyPoliciesException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDeleteLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpDeleteLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDeleteLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDeleteLoadBalancer(response, &metadata) + } + output := &DeleteLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DeleteLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDeleteLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDeleteLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDeleteLoadBalancerListeners struct { +} + +func (*awsAwsquery_deserializeOpDeleteLoadBalancerListeners) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDeleteLoadBalancerListeners) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDeleteLoadBalancerListeners(response, &metadata) + } + output := &DeleteLoadBalancerListenersOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DeleteLoadBalancerListenersResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDeleteLoadBalancerListenersOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDeleteLoadBalancerListeners(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDeleteLoadBalancerPolicy struct { +} + +func (*awsAwsquery_deserializeOpDeleteLoadBalancerPolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDeleteLoadBalancerPolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDeleteLoadBalancerPolicy(response, &metadata) + } + output := &DeleteLoadBalancerPolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DeleteLoadBalancerPolicyResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDeleteLoadBalancerPolicyOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDeleteLoadBalancerPolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDeregisterInstancesFromLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpDeregisterInstancesFromLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDeregisterInstancesFromLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDeregisterInstancesFromLoadBalancer(response, &metadata) + } + output := &DeregisterInstancesFromLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DeregisterInstancesFromLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDeregisterInstancesFromLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDeregisterInstancesFromLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidInstance", errorCode): + return awsAwsquery_deserializeErrorInvalidEndPointException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeAccountLimits struct { +} + +func (*awsAwsquery_deserializeOpDescribeAccountLimits) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeAccountLimits) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeAccountLimits(response, &metadata) + } + output := &DescribeAccountLimitsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeAccountLimitsResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeAccountLimitsOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeAccountLimits(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeInstanceHealth struct { +} + +func (*awsAwsquery_deserializeOpDescribeInstanceHealth) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeInstanceHealth) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeInstanceHealth(response, &metadata) + } + output := &DescribeInstanceHealthOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeInstanceHealthResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeInstanceHealthOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeInstanceHealth(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidInstance", errorCode): + return awsAwsquery_deserializeErrorInvalidEndPointException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeLoadBalancerAttributes struct { +} + +func (*awsAwsquery_deserializeOpDescribeLoadBalancerAttributes) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeLoadBalancerAttributes) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeLoadBalancerAttributes(response, &metadata) + } + output := &DescribeLoadBalancerAttributesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeLoadBalancerAttributesResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeLoadBalancerAttributesOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeLoadBalancerAttributes(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerAttributeNotFound", errorCode): + return awsAwsquery_deserializeErrorLoadBalancerAttributeNotFoundException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeLoadBalancerPolicies struct { +} + +func (*awsAwsquery_deserializeOpDescribeLoadBalancerPolicies) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeLoadBalancerPolicies) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeLoadBalancerPolicies(response, &metadata) + } + output := &DescribeLoadBalancerPoliciesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeLoadBalancerPoliciesResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeLoadBalancerPoliciesOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeLoadBalancerPolicies(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("PolicyNotFound", errorCode): + return awsAwsquery_deserializeErrorPolicyNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeLoadBalancerPolicyTypes struct { +} + +func (*awsAwsquery_deserializeOpDescribeLoadBalancerPolicyTypes) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeLoadBalancerPolicyTypes) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeLoadBalancerPolicyTypes(response, &metadata) + } + output := &DescribeLoadBalancerPolicyTypesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeLoadBalancerPolicyTypesResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeLoadBalancerPolicyTypesOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeLoadBalancerPolicyTypes(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("PolicyTypeNotFound", errorCode): + return awsAwsquery_deserializeErrorPolicyTypeNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeLoadBalancers struct { +} + +func (*awsAwsquery_deserializeOpDescribeLoadBalancers) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeLoadBalancers) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeLoadBalancers(response, &metadata) + } + output := &DescribeLoadBalancersOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeLoadBalancersResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeLoadBalancersOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeLoadBalancers(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("DependencyThrottle", errorCode): + return awsAwsquery_deserializeErrorDependencyThrottleException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDescribeTags struct { +} + +func (*awsAwsquery_deserializeOpDescribeTags) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDescribeTags) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDescribeTags(response, &metadata) + } + output := &DescribeTagsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DescribeTagsResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDescribeTagsOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDescribeTags(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDetachLoadBalancerFromSubnets struct { +} + +func (*awsAwsquery_deserializeOpDetachLoadBalancerFromSubnets) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDetachLoadBalancerFromSubnets) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDetachLoadBalancerFromSubnets(response, &metadata) + } + output := &DetachLoadBalancerFromSubnetsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DetachLoadBalancerFromSubnetsResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDetachLoadBalancerFromSubnetsOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDetachLoadBalancerFromSubnets(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpDisableAvailabilityZonesForLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpDisableAvailabilityZonesForLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpDisableAvailabilityZonesForLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorDisableAvailabilityZonesForLoadBalancer(response, &metadata) + } + output := &DisableAvailabilityZonesForLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("DisableAvailabilityZonesForLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentDisableAvailabilityZonesForLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorDisableAvailabilityZonesForLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpEnableAvailabilityZonesForLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpEnableAvailabilityZonesForLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpEnableAvailabilityZonesForLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorEnableAvailabilityZonesForLoadBalancer(response, &metadata) + } + output := &EnableAvailabilityZonesForLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("EnableAvailabilityZonesForLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentEnableAvailabilityZonesForLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorEnableAvailabilityZonesForLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpModifyLoadBalancerAttributes struct { +} + +func (*awsAwsquery_deserializeOpModifyLoadBalancerAttributes) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpModifyLoadBalancerAttributes) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorModifyLoadBalancerAttributes(response, &metadata) + } + output := &ModifyLoadBalancerAttributesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("ModifyLoadBalancerAttributesResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentModifyLoadBalancerAttributesOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorModifyLoadBalancerAttributes(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerAttributeNotFound", errorCode): + return awsAwsquery_deserializeErrorLoadBalancerAttributeNotFoundException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpRegisterInstancesWithLoadBalancer struct { +} + +func (*awsAwsquery_deserializeOpRegisterInstancesWithLoadBalancer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpRegisterInstancesWithLoadBalancer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorRegisterInstancesWithLoadBalancer(response, &metadata) + } + output := &RegisterInstancesWithLoadBalancerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("RegisterInstancesWithLoadBalancerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentRegisterInstancesWithLoadBalancerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorRegisterInstancesWithLoadBalancer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidInstance", errorCode): + return awsAwsquery_deserializeErrorInvalidEndPointException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpRemoveTags struct { +} + +func (*awsAwsquery_deserializeOpRemoveTags) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpRemoveTags) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorRemoveTags(response, &metadata) + } + output := &RemoveTagsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("RemoveTagsResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentRemoveTagsOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorRemoveTags(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpSetLoadBalancerListenerSSLCertificate struct { +} + +func (*awsAwsquery_deserializeOpSetLoadBalancerListenerSSLCertificate) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpSetLoadBalancerListenerSSLCertificate) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorSetLoadBalancerListenerSSLCertificate(response, &metadata) + } + output := &SetLoadBalancerListenerSSLCertificateOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("SetLoadBalancerListenerSSLCertificateResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentSetLoadBalancerListenerSSLCertificateOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorSetLoadBalancerListenerSSLCertificate(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("CertificateNotFound", errorCode): + return awsAwsquery_deserializeErrorCertificateNotFoundException(response, errorBody) + + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("ListenerNotFound", errorCode): + return awsAwsquery_deserializeErrorListenerNotFoundException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("UnsupportedProtocol", errorCode): + return awsAwsquery_deserializeErrorUnsupportedProtocolException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpSetLoadBalancerPoliciesForBackendServer struct { +} + +func (*awsAwsquery_deserializeOpSetLoadBalancerPoliciesForBackendServer) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpSetLoadBalancerPoliciesForBackendServer) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorSetLoadBalancerPoliciesForBackendServer(response, &metadata) + } + output := &SetLoadBalancerPoliciesForBackendServerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("SetLoadBalancerPoliciesForBackendServerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentSetLoadBalancerPoliciesForBackendServerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorSetLoadBalancerPoliciesForBackendServer(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("PolicyNotFound", errorCode): + return awsAwsquery_deserializeErrorPolicyNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsquery_deserializeOpSetLoadBalancerPoliciesOfListener struct { +} + +func (*awsAwsquery_deserializeOpSetLoadBalancerPoliciesOfListener) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsquery_deserializeOpSetLoadBalancerPoliciesOfListener) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + defer func() { smithyhttp.CloseResponseBody(ctx, response, false, err) }() + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsquery_deserializeOpErrorSetLoadBalancerPoliciesOfListener(response, &metadata) + } + output := &SetLoadBalancerPoliciesOfListenerOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(response.Body, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return out, metadata, nil + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("SetLoadBalancerPoliciesOfListenerResult") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeOpDocumentSetLoadBalancerPoliciesOfListenerOutput(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsquery_deserializeOpErrorSetLoadBalancerPoliciesOfListener(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + errorComponents, err := awsxml.GetErrorResponseComponents(errorBody, false) + if err != nil { + return err + } + if reqID := errorComponents.RequestID; len(reqID) != 0 { + awsmiddleware.SetRequestIDMetadata(metadata, reqID) + } + if len(errorComponents.Code) != 0 { + errorCode = errorComponents.Code + } + if len(errorComponents.Message) != 0 { + errorMessage = errorComponents.Message + } + errorBody.Seek(0, io.SeekStart) + switch { + case strings.EqualFold("InvalidConfigurationRequest", errorCode): + return awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response, errorBody) + + case strings.EqualFold("ListenerNotFound", errorCode): + return awsAwsquery_deserializeErrorListenerNotFoundException(response, errorBody) + + case strings.EqualFold("LoadBalancerNotFound", errorCode): + return awsAwsquery_deserializeErrorAccessPointNotFoundException(response, errorBody) + + case strings.EqualFold("PolicyNotFound", errorCode): + return awsAwsquery_deserializeErrorPolicyNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsAwsquery_deserializeErrorAccessPointNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.AccessPointNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentAccessPointNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorCertificateNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.CertificateNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentCertificateNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorDependencyThrottleException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.DependencyThrottleException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentDependencyThrottleException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorDuplicateAccessPointNameException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.DuplicateAccessPointNameException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentDuplicateAccessPointNameException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorDuplicateListenerException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.DuplicateListenerException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentDuplicateListenerException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorDuplicatePolicyNameException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.DuplicatePolicyNameException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentDuplicatePolicyNameException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorDuplicateTagKeysException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.DuplicateTagKeysException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentDuplicateTagKeysException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorInvalidConfigurationRequestException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InvalidConfigurationRequestException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentInvalidConfigurationRequestException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorInvalidEndPointException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InvalidEndPointException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentInvalidEndPointException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorInvalidSchemeException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InvalidSchemeException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentInvalidSchemeException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorInvalidSecurityGroupException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InvalidSecurityGroupException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentInvalidSecurityGroupException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorInvalidSubnetException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InvalidSubnetException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentInvalidSubnetException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorListenerNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ListenerNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentListenerNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorLoadBalancerAttributeNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.LoadBalancerAttributeNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentLoadBalancerAttributeNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorOperationNotPermittedException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.OperationNotPermittedException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentOperationNotPermittedException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorPolicyNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.PolicyNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentPolicyNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorPolicyTypeNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.PolicyTypeNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentPolicyTypeNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorSubnetNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.SubnetNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentSubnetNotFoundException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorTooManyAccessPointsException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.TooManyAccessPointsException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentTooManyAccessPointsException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorTooManyPoliciesException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.TooManyPoliciesException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentTooManyPoliciesException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorTooManyTagsException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.TooManyTagsException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentTooManyTagsException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeErrorUnsupportedProtocolException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.UnsupportedProtocolException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + body := io.TeeReader(errorBody, ringBuffer) + rootDecoder := xml.NewDecoder(body) + t, err := smithyxml.FetchRootElement(rootDecoder) + if err == io.EOF { + return output + } + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder := smithyxml.WrapNodeDecoder(rootDecoder, t) + t, err = decoder.GetElement("Error") + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + decoder = smithyxml.WrapNodeDecoder(decoder.Decoder, t) + err = awsAwsquery_deserializeDocumentUnsupportedProtocolException(&output, decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + return output +} + +func awsAwsquery_deserializeDocumentAccessLog(v **types.AccessLog, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.AccessLog + if *v == nil { + sv = &types.AccessLog{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("EmitInterval", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.EmitInterval = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("Enabled", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv, err := strconv.ParseBool(string(val)) + if err != nil { + return fmt.Errorf("expected AccessLogEnabled to be of type *bool, got %T instead", val) + } + sv.Enabled = xtv + } + + case strings.EqualFold("S3BucketName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.S3BucketName = ptr.String(xtv) + } + + case strings.EqualFold("S3BucketPrefix", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.S3BucketPrefix = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAccessPointNotFoundException(v **types.AccessPointNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.AccessPointNotFoundException + if *v == nil { + sv = &types.AccessPointNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAdditionalAttribute(v **types.AdditionalAttribute, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.AdditionalAttribute + if *v == nil { + sv = &types.AdditionalAttribute{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Key", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Key = ptr.String(xtv) + } + + case strings.EqualFold("Value", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Value = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAdditionalAttributes(v *[]types.AdditionalAttribute, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.AdditionalAttribute + if *v == nil { + sv = make([]types.AdditionalAttribute, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.AdditionalAttribute + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentAdditionalAttribute(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAdditionalAttributesUnwrapped(v *[]types.AdditionalAttribute, decoder smithyxml.NodeDecoder) error { + var sv []types.AdditionalAttribute + if *v == nil { + sv = make([]types.AdditionalAttribute, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.AdditionalAttribute + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentAdditionalAttribute(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentAppCookieStickinessPolicies(v *[]types.AppCookieStickinessPolicy, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.AppCookieStickinessPolicy + if *v == nil { + sv = make([]types.AppCookieStickinessPolicy, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.AppCookieStickinessPolicy + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentAppCookieStickinessPolicy(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAppCookieStickinessPoliciesUnwrapped(v *[]types.AppCookieStickinessPolicy, decoder smithyxml.NodeDecoder) error { + var sv []types.AppCookieStickinessPolicy + if *v == nil { + sv = make([]types.AppCookieStickinessPolicy, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.AppCookieStickinessPolicy + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentAppCookieStickinessPolicy(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentAppCookieStickinessPolicy(v **types.AppCookieStickinessPolicy, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.AppCookieStickinessPolicy + if *v == nil { + sv = &types.AppCookieStickinessPolicy{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("CookieName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.CookieName = ptr.String(xtv) + } + + case strings.EqualFold("PolicyName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.PolicyName = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAvailabilityZones(v *[]string, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + memberDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + decoder = memberDecoder + switch { + case strings.EqualFold("member", t.Name.Local): + var col string + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + col = xtv + } + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentAvailabilityZonesUnwrapped(v *[]string, decoder smithyxml.NodeDecoder) error { + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + switch { + default: + var mv string + t := decoder.StartEl + _ = t + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + mv = xtv + } + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentBackendServerDescription(v **types.BackendServerDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.BackendServerDescription + if *v == nil { + sv = &types.BackendServerDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("InstancePort", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.InstancePort = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("PolicyNames", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyNames(&sv.PolicyNames, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentBackendServerDescriptions(v *[]types.BackendServerDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.BackendServerDescription + if *v == nil { + sv = make([]types.BackendServerDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.BackendServerDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentBackendServerDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentBackendServerDescriptionsUnwrapped(v *[]types.BackendServerDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.BackendServerDescription + if *v == nil { + sv = make([]types.BackendServerDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.BackendServerDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentBackendServerDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentCertificateNotFoundException(v **types.CertificateNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.CertificateNotFoundException + if *v == nil { + sv = &types.CertificateNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentConnectionDraining(v **types.ConnectionDraining, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.ConnectionDraining + if *v == nil { + sv = &types.ConnectionDraining{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Enabled", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv, err := strconv.ParseBool(string(val)) + if err != nil { + return fmt.Errorf("expected ConnectionDrainingEnabled to be of type *bool, got %T instead", val) + } + sv.Enabled = xtv + } + + case strings.EqualFold("Timeout", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.Timeout = ptr.Int32(int32(i64)) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentConnectionSettings(v **types.ConnectionSettings, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.ConnectionSettings + if *v == nil { + sv = &types.ConnectionSettings{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("IdleTimeout", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.IdleTimeout = ptr.Int32(int32(i64)) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentCrossZoneLoadBalancing(v **types.CrossZoneLoadBalancing, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.CrossZoneLoadBalancing + if *v == nil { + sv = &types.CrossZoneLoadBalancing{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Enabled", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv, err := strconv.ParseBool(string(val)) + if err != nil { + return fmt.Errorf("expected CrossZoneLoadBalancingEnabled to be of type *bool, got %T instead", val) + } + sv.Enabled = xtv + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentDependencyThrottleException(v **types.DependencyThrottleException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.DependencyThrottleException + if *v == nil { + sv = &types.DependencyThrottleException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentDuplicateAccessPointNameException(v **types.DuplicateAccessPointNameException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.DuplicateAccessPointNameException + if *v == nil { + sv = &types.DuplicateAccessPointNameException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentDuplicateListenerException(v **types.DuplicateListenerException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.DuplicateListenerException + if *v == nil { + sv = &types.DuplicateListenerException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentDuplicatePolicyNameException(v **types.DuplicatePolicyNameException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.DuplicatePolicyNameException + if *v == nil { + sv = &types.DuplicatePolicyNameException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentDuplicateTagKeysException(v **types.DuplicateTagKeysException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.DuplicateTagKeysException + if *v == nil { + sv = &types.DuplicateTagKeysException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentHealthCheck(v **types.HealthCheck, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.HealthCheck + if *v == nil { + sv = &types.HealthCheck{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("HealthyThreshold", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.HealthyThreshold = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("Interval", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.Interval = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("Target", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Target = ptr.String(xtv) + } + + case strings.EqualFold("Timeout", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.Timeout = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("UnhealthyThreshold", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.UnhealthyThreshold = ptr.Int32(int32(i64)) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInstance(v **types.Instance, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.Instance + if *v == nil { + sv = &types.Instance{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("InstanceId", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.InstanceId = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInstances(v *[]types.Instance, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.Instance + if *v == nil { + sv = make([]types.Instance, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.Instance + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentInstance(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInstancesUnwrapped(v *[]types.Instance, decoder smithyxml.NodeDecoder) error { + var sv []types.Instance + if *v == nil { + sv = make([]types.Instance, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.Instance + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentInstance(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentInstanceState(v **types.InstanceState, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.InstanceState + if *v == nil { + sv = &types.InstanceState{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Description", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Description = ptr.String(xtv) + } + + case strings.EqualFold("InstanceId", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.InstanceId = ptr.String(xtv) + } + + case strings.EqualFold("ReasonCode", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.ReasonCode = ptr.String(xtv) + } + + case strings.EqualFold("State", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.State = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInstanceStates(v *[]types.InstanceState, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.InstanceState + if *v == nil { + sv = make([]types.InstanceState, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.InstanceState + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentInstanceState(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInstanceStatesUnwrapped(v *[]types.InstanceState, decoder smithyxml.NodeDecoder) error { + var sv []types.InstanceState + if *v == nil { + sv = make([]types.InstanceState, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.InstanceState + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentInstanceState(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentInvalidConfigurationRequestException(v **types.InvalidConfigurationRequestException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.InvalidConfigurationRequestException + if *v == nil { + sv = &types.InvalidConfigurationRequestException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInvalidEndPointException(v **types.InvalidEndPointException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.InvalidEndPointException + if *v == nil { + sv = &types.InvalidEndPointException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInvalidSchemeException(v **types.InvalidSchemeException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.InvalidSchemeException + if *v == nil { + sv = &types.InvalidSchemeException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInvalidSecurityGroupException(v **types.InvalidSecurityGroupException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.InvalidSecurityGroupException + if *v == nil { + sv = &types.InvalidSecurityGroupException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentInvalidSubnetException(v **types.InvalidSubnetException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.InvalidSubnetException + if *v == nil { + sv = &types.InvalidSubnetException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLBCookieStickinessPolicies(v *[]types.LBCookieStickinessPolicy, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.LBCookieStickinessPolicy + if *v == nil { + sv = make([]types.LBCookieStickinessPolicy, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.LBCookieStickinessPolicy + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentLBCookieStickinessPolicy(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLBCookieStickinessPoliciesUnwrapped(v *[]types.LBCookieStickinessPolicy, decoder smithyxml.NodeDecoder) error { + var sv []types.LBCookieStickinessPolicy + if *v == nil { + sv = make([]types.LBCookieStickinessPolicy, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.LBCookieStickinessPolicy + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentLBCookieStickinessPolicy(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentLBCookieStickinessPolicy(v **types.LBCookieStickinessPolicy, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.LBCookieStickinessPolicy + if *v == nil { + sv = &types.LBCookieStickinessPolicy{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("CookieExpirationPeriod", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.CookieExpirationPeriod = ptr.Int64(i64) + } + + case strings.EqualFold("PolicyName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.PolicyName = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLimit(v **types.Limit, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.Limit + if *v == nil { + sv = &types.Limit{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Max", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Max = ptr.String(xtv) + } + + case strings.EqualFold("Name", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Name = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLimits(v *[]types.Limit, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.Limit + if *v == nil { + sv = make([]types.Limit, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.Limit + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentLimit(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLimitsUnwrapped(v *[]types.Limit, decoder smithyxml.NodeDecoder) error { + var sv []types.Limit + if *v == nil { + sv = make([]types.Limit, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.Limit + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentLimit(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentListener(v **types.Listener, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.Listener + if *v == nil { + sv = &types.Listener{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("InstancePort", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.InstancePort = ptr.Int32(int32(i64)) + } + + case strings.EqualFold("InstanceProtocol", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.InstanceProtocol = ptr.String(xtv) + } + + case strings.EqualFold("LoadBalancerPort", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + i64, err := strconv.ParseInt(xtv, 10, 64) + if err != nil { + return err + } + sv.LoadBalancerPort = int32(i64) + } + + case strings.EqualFold("Protocol", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Protocol = ptr.String(xtv) + } + + case strings.EqualFold("SSLCertificateId", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.SSLCertificateId = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentListenerDescription(v **types.ListenerDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.ListenerDescription + if *v == nil { + sv = &types.ListenerDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Listener", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentListener(&sv.Listener, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("PolicyNames", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyNames(&sv.PolicyNames, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentListenerDescriptions(v *[]types.ListenerDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.ListenerDescription + if *v == nil { + sv = make([]types.ListenerDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.ListenerDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentListenerDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentListenerDescriptionsUnwrapped(v *[]types.ListenerDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.ListenerDescription + if *v == nil { + sv = make([]types.ListenerDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.ListenerDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentListenerDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentListenerNotFoundException(v **types.ListenerNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.ListenerNotFoundException + if *v == nil { + sv = &types.ListenerNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLoadBalancerAttributeNotFoundException(v **types.LoadBalancerAttributeNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.LoadBalancerAttributeNotFoundException + if *v == nil { + sv = &types.LoadBalancerAttributeNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLoadBalancerAttributes(v **types.LoadBalancerAttributes, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.LoadBalancerAttributes + if *v == nil { + sv = &types.LoadBalancerAttributes{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AccessLog", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentAccessLog(&sv.AccessLog, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("AdditionalAttributes", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentAdditionalAttributes(&sv.AdditionalAttributes, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("ConnectionDraining", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentConnectionDraining(&sv.ConnectionDraining, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("ConnectionSettings", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentConnectionSettings(&sv.ConnectionSettings, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("CrossZoneLoadBalancing", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentCrossZoneLoadBalancing(&sv.CrossZoneLoadBalancing, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLoadBalancerDescription(v **types.LoadBalancerDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.LoadBalancerDescription + if *v == nil { + sv = &types.LoadBalancerDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AvailabilityZones", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentAvailabilityZones(&sv.AvailabilityZones, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("BackendServerDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentBackendServerDescriptions(&sv.BackendServerDescriptions, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("CanonicalHostedZoneName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.CanonicalHostedZoneName = ptr.String(xtv) + } + + case strings.EqualFold("CanonicalHostedZoneNameID", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.CanonicalHostedZoneNameID = ptr.String(xtv) + } + + case strings.EqualFold("CreatedTime", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + t, err := smithytime.ParseDateTime(xtv) + if err != nil { + return err + } + sv.CreatedTime = ptr.Time(t) + } + + case strings.EqualFold("DNSName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.DNSName = ptr.String(xtv) + } + + case strings.EqualFold("HealthCheck", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentHealthCheck(&sv.HealthCheck, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("Instances", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentInstances(&sv.Instances, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("ListenerDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentListenerDescriptions(&sv.ListenerDescriptions, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("LoadBalancerName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.LoadBalancerName = ptr.String(xtv) + } + + case strings.EqualFold("Policies", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicies(&sv.Policies, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("Scheme", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Scheme = ptr.String(xtv) + } + + case strings.EqualFold("SecurityGroups", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentSecurityGroups(&sv.SecurityGroups, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("SourceSecurityGroup", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentSourceSecurityGroup(&sv.SourceSecurityGroup, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("Subnets", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentSubnets(&sv.Subnets, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("VPCId", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.VPCId = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLoadBalancerDescriptions(v *[]types.LoadBalancerDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.LoadBalancerDescription + if *v == nil { + sv = make([]types.LoadBalancerDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.LoadBalancerDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentLoadBalancerDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentLoadBalancerDescriptionsUnwrapped(v *[]types.LoadBalancerDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.LoadBalancerDescription + if *v == nil { + sv = make([]types.LoadBalancerDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.LoadBalancerDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentLoadBalancerDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentOperationNotPermittedException(v **types.OperationNotPermittedException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.OperationNotPermittedException + if *v == nil { + sv = &types.OperationNotPermittedException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicies(v **types.Policies, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.Policies + if *v == nil { + sv = &types.Policies{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AppCookieStickinessPolicies", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentAppCookieStickinessPolicies(&sv.AppCookieStickinessPolicies, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("LBCookieStickinessPolicies", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentLBCookieStickinessPolicies(&sv.LBCookieStickinessPolicies, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("OtherPolicies", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyNames(&sv.OtherPolicies, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyAttributeDescription(v **types.PolicyAttributeDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.PolicyAttributeDescription + if *v == nil { + sv = &types.PolicyAttributeDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AttributeName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.AttributeName = ptr.String(xtv) + } + + case strings.EqualFold("AttributeValue", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.AttributeValue = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyAttributeDescriptions(v *[]types.PolicyAttributeDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.PolicyAttributeDescription + if *v == nil { + sv = make([]types.PolicyAttributeDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.PolicyAttributeDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentPolicyAttributeDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyAttributeDescriptionsUnwrapped(v *[]types.PolicyAttributeDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.PolicyAttributeDescription + if *v == nil { + sv = make([]types.PolicyAttributeDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.PolicyAttributeDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentPolicyAttributeDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentPolicyAttributeTypeDescription(v **types.PolicyAttributeTypeDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.PolicyAttributeTypeDescription + if *v == nil { + sv = &types.PolicyAttributeTypeDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AttributeName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.AttributeName = ptr.String(xtv) + } + + case strings.EqualFold("AttributeType", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.AttributeType = ptr.String(xtv) + } + + case strings.EqualFold("Cardinality", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Cardinality = ptr.String(xtv) + } + + case strings.EqualFold("DefaultValue", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.DefaultValue = ptr.String(xtv) + } + + case strings.EqualFold("Description", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Description = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyAttributeTypeDescriptions(v *[]types.PolicyAttributeTypeDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.PolicyAttributeTypeDescription + if *v == nil { + sv = make([]types.PolicyAttributeTypeDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.PolicyAttributeTypeDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentPolicyAttributeTypeDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyAttributeTypeDescriptionsUnwrapped(v *[]types.PolicyAttributeTypeDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.PolicyAttributeTypeDescription + if *v == nil { + sv = make([]types.PolicyAttributeTypeDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.PolicyAttributeTypeDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentPolicyAttributeTypeDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentPolicyDescription(v **types.PolicyDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.PolicyDescription + if *v == nil { + sv = &types.PolicyDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("PolicyAttributeDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyAttributeDescriptions(&sv.PolicyAttributeDescriptions, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("PolicyName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.PolicyName = ptr.String(xtv) + } + + case strings.EqualFold("PolicyTypeName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.PolicyTypeName = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyDescriptions(v *[]types.PolicyDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.PolicyDescription + if *v == nil { + sv = make([]types.PolicyDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.PolicyDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentPolicyDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyDescriptionsUnwrapped(v *[]types.PolicyDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.PolicyDescription + if *v == nil { + sv = make([]types.PolicyDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.PolicyDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentPolicyDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentPolicyNames(v *[]string, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + memberDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + decoder = memberDecoder + switch { + case strings.EqualFold("member", t.Name.Local): + var col string + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + col = xtv + } + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyNamesUnwrapped(v *[]string, decoder smithyxml.NodeDecoder) error { + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + switch { + default: + var mv string + t := decoder.StartEl + _ = t + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + mv = xtv + } + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentPolicyNotFoundException(v **types.PolicyNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.PolicyNotFoundException + if *v == nil { + sv = &types.PolicyNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyTypeDescription(v **types.PolicyTypeDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.PolicyTypeDescription + if *v == nil { + sv = &types.PolicyTypeDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Description", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Description = ptr.String(xtv) + } + + case strings.EqualFold("PolicyAttributeTypeDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyAttributeTypeDescriptions(&sv.PolicyAttributeTypeDescriptions, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("PolicyTypeName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.PolicyTypeName = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyTypeDescriptions(v *[]types.PolicyTypeDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.PolicyTypeDescription + if *v == nil { + sv = make([]types.PolicyTypeDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.PolicyTypeDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentPolicyTypeDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentPolicyTypeDescriptionsUnwrapped(v *[]types.PolicyTypeDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.PolicyTypeDescription + if *v == nil { + sv = make([]types.PolicyTypeDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.PolicyTypeDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentPolicyTypeDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentPolicyTypeNotFoundException(v **types.PolicyTypeNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.PolicyTypeNotFoundException + if *v == nil { + sv = &types.PolicyTypeNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentSecurityGroups(v *[]string, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + memberDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + decoder = memberDecoder + switch { + case strings.EqualFold("member", t.Name.Local): + var col string + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + col = xtv + } + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentSecurityGroupsUnwrapped(v *[]string, decoder smithyxml.NodeDecoder) error { + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + switch { + default: + var mv string + t := decoder.StartEl + _ = t + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + mv = xtv + } + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentSourceSecurityGroup(v **types.SourceSecurityGroup, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.SourceSecurityGroup + if *v == nil { + sv = &types.SourceSecurityGroup{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("GroupName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.GroupName = ptr.String(xtv) + } + + case strings.EqualFold("OwnerAlias", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.OwnerAlias = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentSubnetNotFoundException(v **types.SubnetNotFoundException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.SubnetNotFoundException + if *v == nil { + sv = &types.SubnetNotFoundException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentSubnets(v *[]string, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + memberDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + decoder = memberDecoder + switch { + case strings.EqualFold("member", t.Name.Local): + var col string + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + col = xtv + } + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentSubnetsUnwrapped(v *[]string, decoder smithyxml.NodeDecoder) error { + var sv []string + if *v == nil { + sv = make([]string, 0) + } else { + sv = *v + } + + switch { + default: + var mv string + t := decoder.StartEl + _ = t + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + mv = xtv + } + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentTag(v **types.Tag, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.Tag + if *v == nil { + sv = &types.Tag{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Key", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Key = ptr.String(xtv) + } + + case strings.EqualFold("Value", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Value = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentTagDescription(v **types.TagDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.TagDescription + if *v == nil { + sv = &types.TagDescription{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("LoadBalancerName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.LoadBalancerName = ptr.String(xtv) + } + + case strings.EqualFold("Tags", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentTagList(&sv.Tags, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentTagDescriptions(v *[]types.TagDescription, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.TagDescription + if *v == nil { + sv = make([]types.TagDescription, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.TagDescription + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentTagDescription(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentTagDescriptionsUnwrapped(v *[]types.TagDescription, decoder smithyxml.NodeDecoder) error { + var sv []types.TagDescription + if *v == nil { + sv = make([]types.TagDescription, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.TagDescription + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentTagDescription(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentTagList(v *[]types.Tag, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv []types.Tag + if *v == nil { + sv = make([]types.Tag, 0) + } else { + sv = *v + } + + originalDecoder := decoder + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + switch { + case strings.EqualFold("member", t.Name.Local): + var col types.Tag + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &col + if err := awsAwsquery_deserializeDocumentTag(&destAddr, nodeDecoder); err != nil { + return err + } + col = *destAddr + sv = append(sv, col) + + default: + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentTagListUnwrapped(v *[]types.Tag, decoder smithyxml.NodeDecoder) error { + var sv []types.Tag + if *v == nil { + sv = make([]types.Tag, 0) + } else { + sv = *v + } + + switch { + default: + var mv types.Tag + t := decoder.StartEl + _ = t + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + destAddr := &mv + if err := awsAwsquery_deserializeDocumentTag(&destAddr, nodeDecoder); err != nil { + return err + } + mv = *destAddr + sv = append(sv, mv) + } + *v = sv + return nil +} +func awsAwsquery_deserializeDocumentTooManyAccessPointsException(v **types.TooManyAccessPointsException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.TooManyAccessPointsException + if *v == nil { + sv = &types.TooManyAccessPointsException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentTooManyPoliciesException(v **types.TooManyPoliciesException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.TooManyPoliciesException + if *v == nil { + sv = &types.TooManyPoliciesException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentTooManyTagsException(v **types.TooManyTagsException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.TooManyTagsException + if *v == nil { + sv = &types.TooManyTagsException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeDocumentUnsupportedProtocolException(v **types.UnsupportedProtocolException, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *types.UnsupportedProtocolException + if *v == nil { + sv = &types.UnsupportedProtocolException{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Message", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.Message = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentAddTagsOutput(v **AddTagsOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *AddTagsOutput + if *v == nil { + sv = &AddTagsOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentApplySecurityGroupsToLoadBalancerOutput(v **ApplySecurityGroupsToLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *ApplySecurityGroupsToLoadBalancerOutput + if *v == nil { + sv = &ApplySecurityGroupsToLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("SecurityGroups", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentSecurityGroups(&sv.SecurityGroups, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentAttachLoadBalancerToSubnetsOutput(v **AttachLoadBalancerToSubnetsOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *AttachLoadBalancerToSubnetsOutput + if *v == nil { + sv = &AttachLoadBalancerToSubnetsOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Subnets", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentSubnets(&sv.Subnets, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentConfigureHealthCheckOutput(v **ConfigureHealthCheckOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *ConfigureHealthCheckOutput + if *v == nil { + sv = &ConfigureHealthCheckOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("HealthCheck", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentHealthCheck(&sv.HealthCheck, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentCreateAppCookieStickinessPolicyOutput(v **CreateAppCookieStickinessPolicyOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *CreateAppCookieStickinessPolicyOutput + if *v == nil { + sv = &CreateAppCookieStickinessPolicyOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentCreateLBCookieStickinessPolicyOutput(v **CreateLBCookieStickinessPolicyOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *CreateLBCookieStickinessPolicyOutput + if *v == nil { + sv = &CreateLBCookieStickinessPolicyOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentCreateLoadBalancerListenersOutput(v **CreateLoadBalancerListenersOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *CreateLoadBalancerListenersOutput + if *v == nil { + sv = &CreateLoadBalancerListenersOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentCreateLoadBalancerOutput(v **CreateLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *CreateLoadBalancerOutput + if *v == nil { + sv = &CreateLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("DNSName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.DNSName = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentCreateLoadBalancerPolicyOutput(v **CreateLoadBalancerPolicyOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *CreateLoadBalancerPolicyOutput + if *v == nil { + sv = &CreateLoadBalancerPolicyOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDeleteLoadBalancerListenersOutput(v **DeleteLoadBalancerListenersOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DeleteLoadBalancerListenersOutput + if *v == nil { + sv = &DeleteLoadBalancerListenersOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDeleteLoadBalancerOutput(v **DeleteLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DeleteLoadBalancerOutput + if *v == nil { + sv = &DeleteLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDeleteLoadBalancerPolicyOutput(v **DeleteLoadBalancerPolicyOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DeleteLoadBalancerPolicyOutput + if *v == nil { + sv = &DeleteLoadBalancerPolicyOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDeregisterInstancesFromLoadBalancerOutput(v **DeregisterInstancesFromLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DeregisterInstancesFromLoadBalancerOutput + if *v == nil { + sv = &DeregisterInstancesFromLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Instances", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentInstances(&sv.Instances, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeAccountLimitsOutput(v **DescribeAccountLimitsOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeAccountLimitsOutput + if *v == nil { + sv = &DescribeAccountLimitsOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Limits", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentLimits(&sv.Limits, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("NextMarker", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.NextMarker = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeInstanceHealthOutput(v **DescribeInstanceHealthOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeInstanceHealthOutput + if *v == nil { + sv = &DescribeInstanceHealthOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("InstanceStates", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentInstanceStates(&sv.InstanceStates, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeLoadBalancerAttributesOutput(v **DescribeLoadBalancerAttributesOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeLoadBalancerAttributesOutput + if *v == nil { + sv = &DescribeLoadBalancerAttributesOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("LoadBalancerAttributes", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentLoadBalancerAttributes(&sv.LoadBalancerAttributes, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeLoadBalancerPoliciesOutput(v **DescribeLoadBalancerPoliciesOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeLoadBalancerPoliciesOutput + if *v == nil { + sv = &DescribeLoadBalancerPoliciesOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("PolicyDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyDescriptions(&sv.PolicyDescriptions, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeLoadBalancerPolicyTypesOutput(v **DescribeLoadBalancerPolicyTypesOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeLoadBalancerPolicyTypesOutput + if *v == nil { + sv = &DescribeLoadBalancerPolicyTypesOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("PolicyTypeDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentPolicyTypeDescriptions(&sv.PolicyTypeDescriptions, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeLoadBalancersOutput(v **DescribeLoadBalancersOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeLoadBalancersOutput + if *v == nil { + sv = &DescribeLoadBalancersOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("LoadBalancerDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentLoadBalancerDescriptions(&sv.LoadBalancerDescriptions, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("NextMarker", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.NextMarker = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDescribeTagsOutput(v **DescribeTagsOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DescribeTagsOutput + if *v == nil { + sv = &DescribeTagsOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("TagDescriptions", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentTagDescriptions(&sv.TagDescriptions, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDetachLoadBalancerFromSubnetsOutput(v **DetachLoadBalancerFromSubnetsOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DetachLoadBalancerFromSubnetsOutput + if *v == nil { + sv = &DetachLoadBalancerFromSubnetsOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Subnets", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentSubnets(&sv.Subnets, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentDisableAvailabilityZonesForLoadBalancerOutput(v **DisableAvailabilityZonesForLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *DisableAvailabilityZonesForLoadBalancerOutput + if *v == nil { + sv = &DisableAvailabilityZonesForLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AvailabilityZones", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentAvailabilityZones(&sv.AvailabilityZones, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentEnableAvailabilityZonesForLoadBalancerOutput(v **EnableAvailabilityZonesForLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *EnableAvailabilityZonesForLoadBalancerOutput + if *v == nil { + sv = &EnableAvailabilityZonesForLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("AvailabilityZones", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentAvailabilityZones(&sv.AvailabilityZones, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentModifyLoadBalancerAttributesOutput(v **ModifyLoadBalancerAttributesOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *ModifyLoadBalancerAttributesOutput + if *v == nil { + sv = &ModifyLoadBalancerAttributesOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("LoadBalancerAttributes", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentLoadBalancerAttributes(&sv.LoadBalancerAttributes, nodeDecoder); err != nil { + return err + } + + case strings.EqualFold("LoadBalancerName", t.Name.Local): + val, err := decoder.Value() + if err != nil { + return err + } + if val == nil { + break + } + { + xtv := string(val) + sv.LoadBalancerName = ptr.String(xtv) + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentRegisterInstancesWithLoadBalancerOutput(v **RegisterInstancesWithLoadBalancerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *RegisterInstancesWithLoadBalancerOutput + if *v == nil { + sv = &RegisterInstancesWithLoadBalancerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + case strings.EqualFold("Instances", t.Name.Local): + nodeDecoder := smithyxml.WrapNodeDecoder(decoder.Decoder, t) + if err := awsAwsquery_deserializeDocumentInstances(&sv.Instances, nodeDecoder); err != nil { + return err + } + + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentRemoveTagsOutput(v **RemoveTagsOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *RemoveTagsOutput + if *v == nil { + sv = &RemoveTagsOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentSetLoadBalancerListenerSSLCertificateOutput(v **SetLoadBalancerListenerSSLCertificateOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *SetLoadBalancerListenerSSLCertificateOutput + if *v == nil { + sv = &SetLoadBalancerListenerSSLCertificateOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentSetLoadBalancerPoliciesForBackendServerOutput(v **SetLoadBalancerPoliciesForBackendServerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *SetLoadBalancerPoliciesForBackendServerOutput + if *v == nil { + sv = &SetLoadBalancerPoliciesForBackendServerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} + +func awsAwsquery_deserializeOpDocumentSetLoadBalancerPoliciesOfListenerOutput(v **SetLoadBalancerPoliciesOfListenerOutput, decoder smithyxml.NodeDecoder) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + var sv *SetLoadBalancerPoliciesOfListenerOutput + if *v == nil { + sv = &SetLoadBalancerPoliciesOfListenerOutput{} + } else { + sv = *v + } + + for { + t, done, err := decoder.Token() + if err != nil { + return err + } + if done { + break + } + originalDecoder := decoder + decoder = smithyxml.WrapNodeDecoder(originalDecoder.Decoder, t) + switch { + default: + // Do nothing and ignore the unexpected tag element + err = decoder.Decoder.Skip() + if err != nil { + return err + } + + } + decoder = originalDecoder + } + *v = sv + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/doc.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/doc.go new file mode 100644 index 000000000..426f94020 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/doc.go @@ -0,0 +1,33 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +// Package elasticloadbalancing provides the API client, operations, and parameter +// types for Elastic Load Balancing. +// +// # Elastic Load Balancing +// +// A load balancer can distribute incoming traffic across your EC2 instances. This +// enables you to increase the availability of your application. The load balancer +// also monitors the health of its registered instances and ensures that it routes +// traffic only to healthy instances. You configure your load balancer to accept +// incoming traffic by specifying one or more listeners, which are configured with +// a protocol and port number for connections from clients to the load balancer and +// a protocol and port number for connections from the load balancer to the +// instances. +// +// Elastic Load Balancing supports three types of load balancers: Application Load +// Balancers, Network Load Balancers, and Classic Load Balancers. You can select a +// load balancer based on your application needs. For more information, see the [Elastic Load Balancing User Guide]. +// +// This reference covers the 2012-06-01 API, which supports Classic Load +// Balancers. The 2015-12-01 API supports Application Load Balancers and Network +// Load Balancers. +// +// To get started, create a load balancer with one or more listeners using CreateLoadBalancer. +// Register your instances with the load balancer using RegisterInstancesWithLoadBalancer. +// +// All Elastic Load Balancing operations are idempotent, which means that they +// complete at most one time. If you repeat an operation, it succeeds with a 200 OK +// response code. +// +// [Elastic Load Balancing User Guide]: https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/ +package elasticloadbalancing diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/endpoints.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/endpoints.go new file mode 100644 index 000000000..30a23f14f --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/endpoints.go @@ -0,0 +1,570 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources" + "github.com/aws/aws-sdk-go-v2/internal/endpoints" + "github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn" + internalendpoints "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/internal/endpoints" + smithyauth "github.com/aws/smithy-go/auth" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/bdd" + "github.com/aws/smithy-go/endpoints/private/rulesfn" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net/http" + "net/url" + "os" + "strings" +) + +// EndpointResolverOptions is the service endpoint resolver options +type EndpointResolverOptions = internalendpoints.Options + +// EndpointResolver interface for resolving service endpoints. +type EndpointResolver interface { + ResolveEndpoint(region string, options EndpointResolverOptions) (aws.Endpoint, error) +} + +var _ EndpointResolver = &internalendpoints.Resolver{} + +// NewDefaultEndpointResolver constructs a new service endpoint resolver +func NewDefaultEndpointResolver() *internalendpoints.Resolver { + return internalendpoints.New() +} + +// EndpointResolverFunc is a helper utility that wraps a function so it satisfies +// the EndpointResolver interface. This is useful when you want to add additional +// endpoint resolving logic, or stub out specific endpoints with custom values. +type EndpointResolverFunc func(region string, options EndpointResolverOptions) (aws.Endpoint, error) + +func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) { + return fn(region, options) +} + +// EndpointResolverFromURL returns an EndpointResolver configured using the +// provided endpoint url. By default, the resolved endpoint resolver uses the +// client region as signing region, and the endpoint source is set to +// EndpointSourceCustom.You can provide functional options to configure endpoint +// values for the resolved endpoint. +func EndpointResolverFromURL(url string, optFns ...func(*aws.Endpoint)) EndpointResolver { + e := aws.Endpoint{URL: url, Source: aws.EndpointSourceCustom} + for _, fn := range optFns { + fn(&e) + } + + return EndpointResolverFunc( + func(region string, options EndpointResolverOptions) (aws.Endpoint, error) { + if len(e.SigningRegion) == 0 { + e.SigningRegion = region + } + return e, nil + }, + ) +} + +type ResolveEndpoint struct { + Resolver EndpointResolver + Options EndpointResolverOptions +} + +func (*ResolveEndpoint) ID() string { + return "ResolveEndpoint" +} + +func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if !awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.Resolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + eo := m.Options + eo.Logger = middleware.GetLogger(ctx) + + var endpoint aws.Endpoint + endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), eo) + if err != nil { + nf := (&aws.EndpointNotFoundError{}) + if errors.As(err, &nf) { + ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, false) + return next.HandleSerialize(ctx, in) + } + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL, err = url.Parse(endpoint.URL) + if err != nil { + return out, metadata, fmt.Errorf("failed to parse endpoint URL: %w", err) + } + + if len(awsmiddleware.GetSigningName(ctx)) == 0 { + signingName := endpoint.SigningName + if len(signingName) == 0 { + signingName = "elasticloadbalancing" + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + } + ctx = awsmiddleware.SetEndpointSource(ctx, endpoint.Source) + ctx = smithyhttp.SetHostnameImmutable(ctx, endpoint.HostnameImmutable) + ctx = awsmiddleware.SetSigningRegion(ctx, endpoint.SigningRegion) + ctx = awsmiddleware.SetPartitionID(ctx, endpoint.PartitionID) + return next.HandleSerialize(ctx, in) +} +func addResolveEndpointMiddleware(stack *middleware.Stack, o Options) error { + return stack.Serialize.Insert(&ResolveEndpoint{ + Resolver: o.EndpointResolver, + Options: o.EndpointOptions, + }, "OperationSerializer", middleware.Before) +} + +func removeResolveEndpointMiddleware(stack *middleware.Stack) error { + _, err := stack.Serialize.Remove((&ResolveEndpoint{}).ID()) + return err +} + +type wrappedEndpointResolver struct { + awsResolver aws.EndpointResolverWithOptions +} + +func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) { + return w.awsResolver.ResolveEndpoint(ServiceID, region, options) +} + +type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error) + +func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) { + return a(service, region) +} + +var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil) + +// withEndpointResolver returns an aws.EndpointResolverWithOptions that first delegates endpoint resolution to the awsResolver. +// If awsResolver returns aws.EndpointNotFoundError error, the v1 resolver middleware will swallow the error, +// and set an appropriate context flag such that fallback will occur when EndpointResolverV2 is invoked +// via its middleware. +// +// If another error (besides aws.EndpointNotFoundError) is returned, then that error will be propagated. +func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions) EndpointResolver { + var resolver aws.EndpointResolverWithOptions + + if awsResolverWithOptions != nil { + resolver = awsResolverWithOptions + } else if awsResolver != nil { + resolver = awsEndpointResolverAdaptor(awsResolver.ResolveEndpoint) + } + + return &wrappedEndpointResolver{ + awsResolver: resolver, + } +} + +func finalizeClientEndpointResolverOptions(options *Options) { + options.EndpointOptions.LogDeprecated = options.ClientLogMode.IsDeprecatedUsage() + + if len(options.EndpointOptions.ResolvedRegion) == 0 { + const fipsInfix = "-fips-" + const fipsPrefix = "fips-" + const fipsSuffix = "-fips" + + if strings.Contains(options.Region, fipsInfix) || + strings.Contains(options.Region, fipsPrefix) || + strings.Contains(options.Region, fipsSuffix) { + options.EndpointOptions.ResolvedRegion = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll( + options.Region, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "") + options.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled + } + } + +} + +func resolveEndpointResolverV2(options *Options) { + if options.EndpointResolverV2 == nil { + options.EndpointResolverV2 = NewDefaultEndpointResolverV2() + } +} + +func resolveBaseEndpoint(cfg aws.Config, o *Options) { + if cfg.BaseEndpoint != nil { + o.BaseEndpoint = cfg.BaseEndpoint + } + + _, g := os.LookupEnv("AWS_ENDPOINT_URL") + _, s := os.LookupEnv("AWS_ENDPOINT_URL_ELASTIC_LOAD_BALANCING") + + if g && !s { + return + } + + value, found, err := internalConfig.ResolveServiceBaseEndpoint(context.Background(), "Elastic Load Balancing", cfg.ConfigSources) + if found && err == nil { + o.BaseEndpoint = &value + } +} + +func bindRegion(region string) (*string, error) { + if region == "" { + return nil, nil + } + if !rulesfn.IsValidHostLabel(region, true) { + return nil, fmt.Errorf("invalid input region %s", region) + } + + return aws.String(endpoints.MapFIPSRegion(region)), nil +} + +var _ = rulesfn.StringSlice(nil) + +// EndpointParameters provides the parameters that influence how endpoints are +// resolved. +type EndpointParameters struct { + // The AWS region used to dispatch the request. + // + // Parameter is + // required. + // + // AWS::Region + Region *string + + // When true, use the dual-stack endpoint. If the configured endpoint does not + // support dual-stack, dispatching the request MAY return an error. + // + // Defaults to + // false if no value is provided. + // + // AWS::UseDualStack + UseDualStack *bool + + // When true, send this request to the FIPS-compliant regional endpoint. If the + // configured endpoint does not have a FIPS compliant endpoint, dispatching the + // request will return an error. + // + // Defaults to false if no value is + // provided. + // + // AWS::UseFIPS + UseFIPS *bool + + // Override the endpoint used to send this request + // + // Parameter is + // required. + // + // SDK::Endpoint + Endpoint *string +} + +// ValidateRequired validates required parameters are set. +func (p EndpointParameters) ValidateRequired() error { + if p.UseDualStack == nil { + return fmt.Errorf("parameter UseDualStack is required") + } + + if p.UseFIPS == nil { + return fmt.Errorf("parameter UseFIPS is required") + } + + return nil +} + +// WithDefaults returns a shallow copy of EndpointParameterswith default values +// applied to members where applicable. +func (p EndpointParameters) WithDefaults() EndpointParameters { + if p.UseDualStack == nil { + p.UseDualStack = ptr.Bool(false) + } + + if p.UseFIPS == nil { + p.UseFIPS = ptr.Bool(false) + } + return p +} + +const bddRoot int32 = 2 + +var bddNodes = [42]int32{ + -1, 1, -1, 0, 13, 3, 1, 4, 100000012, 2, 5, 100000012, 3, 8, 6, 4, 7, 100000011, 5, 100000009, 100000010, 4, 11, 9, 6, 10, 100000008, 7, 100000006, 100000007, 5, 12, 100000005, 6, 100000004, 100000005, 3, 100000001, 14, 4, 100000002, 100000003} + +type conditionContext struct { + PartitionResult *awsrulesfn.PartitionConfig +} + +func evalCondition(idx int, params *EndpointParameters, c *conditionContext) bool { + switch idx { + case 0: + return params.Endpoint != nil + case 1: + return params.Region != nil + case 2: + if v := awsrulesfn.GetPartition(*params.Region); v != nil { + c.PartitionResult = v + return true + } + return false + case 3: + return *params.UseFIPS == true + case 4: + return *params.UseDualStack == true + case 5: + return c.PartitionResult.SupportsDualStack == true + case 6: + return c.PartitionResult.SupportsFIPS == true + case 7: + return c.PartitionResult.Name == "aws-us-gov" + } + return false +} + +func resolveResult(idx int32, params *EndpointParameters, c *conditionContext) (smithyendpoints.Endpoint, error) { + switch idx { + case 0: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint resolution failed: no matching rule") + case 1: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + case 2: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + case 3: + uriString := *params.Endpoint + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 4: + uriString := func() string { + var out strings.Builder + out.WriteString("https://elasticloadbalancing-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 5: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + case 6: + uriString := func() string { + var out strings.Builder + out.WriteString("https://elasticloadbalancing.") + out.WriteString(*params.Region) + out.WriteString(".amazonaws.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 7: + uriString := func() string { + var out strings.Builder + out.WriteString("https://elasticloadbalancing-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 8: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + case 9: + uriString := func() string { + var out strings.Builder + out.WriteString("https://elasticloadbalancing.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 10: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + case 11: + uriString := func() string { + var out strings.Builder + out.WriteString("https://elasticloadbalancing.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 12: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + } + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, invalid result index: %d", idx) +} + +// EndpointResolverV2 provides the interface for resolving service endpoints. +type EndpointResolverV2 interface { + ResolveEndpoint(ctx context.Context, params EndpointParameters) ( + smithyendpoints.Endpoint, error, + ) +} + +// resolver provides the implementation for resolving endpoints. +type resolver struct{} + +func NewDefaultEndpointResolverV2() EndpointResolverV2 { + return &resolver{} +} + +// ResolveEndpoint attempts to resolve the endpoint with the provided options, +// returning the endpoint if found. Otherwise an error is returned. +func (r *resolver) ResolveEndpoint( + ctx context.Context, params EndpointParameters, +) ( + endpoint smithyendpoints.Endpoint, err error, +) { + params = params.WithDefaults() + if err = params.ValidateRequired(); err != nil { + return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) + } + + c := &conditionContext{} + ref := bdd.Evaluate(bddNodes[:], bddRoot, func(idx int) bool { + return evalCondition(idx, ¶ms, c) + }) + return resolveResult(ref, ¶ms, c) +} + +type endpointParamsBinder interface { + bindEndpointParams(*EndpointParameters) +} + +func bindEndpointParams(ctx context.Context, input interface{}, options Options) (*EndpointParameters, error) { + params := &EndpointParameters{} + + region, err := bindRegion(options.Region) + if err != nil { + return nil, err + } + params.Region = region + + params.UseDualStack = aws.Bool(options.EndpointOptions.UseDualStackEndpoint == aws.DualStackEndpointStateEnabled) + params.UseFIPS = aws.Bool(options.EndpointOptions.UseFIPSEndpoint == aws.FIPSEndpointStateEnabled) + params.Endpoint = options.BaseEndpoint + + if b, ok := input.(endpointParamsBinder); ok { + b.bindEndpointParams(params) + } + + return params, nil +} + +type resolveEndpointV2Middleware struct { + options Options +} + +func (*resolveEndpointV2Middleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *resolveEndpointV2Middleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "ResolveEndpoint") + defer span.End() + + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleFinalize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.options.EndpointResolverV2 == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params, err := bindEndpointParams(ctx, getOperationInput(ctx), m.options) + if err != nil { + return out, metadata, fmt.Errorf("failed to bind endpoint params, %w", err) + } + endpt, err := timeOperationMetric(ctx, "client.call.resolve_endpoint_duration", + func() (smithyendpoints.Endpoint, error) { + return m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params) + }) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + span.SetProperty("client.call.resolved_endpoint", endpt.URI.String()) + + if endpt.URI.RawPath == "" && req.URL.RawPath != "" { + endpt.URI.RawPath = endpt.URI.Path + } + req.URL.Scheme = endpt.URI.Scheme + req.URL.Host = endpt.URI.Host + req.URL.Path = smithyhttp.JoinPath(endpt.URI.Path, req.URL.Path) + req.URL.RawPath = smithyhttp.JoinPath(endpt.URI.RawPath, req.URL.RawPath) + for k := range endpt.Headers { + req.Header.Set(k, endpt.Headers.Get(k)) + } + + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + opts, _ := smithyauth.GetAuthOptions(&endpt.Properties) + for _, o := range opts { + rscheme.SignerProperties.SetAll(&o.SignerProperties) + } + + span.End() + return next.HandleFinalize(ctx, in) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/generated.json b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/generated.json new file mode 100644 index 000000000..1325f4e11 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/generated.json @@ -0,0 +1,62 @@ +{ + "dependencies": { + "github.com/aws/aws-sdk-go-v2": "v1.4.0", + "github.com/aws/aws-sdk-go-v2/internal/configsources": "v0.0.0-00010101000000-000000000000", + "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2": "v2.0.0-00010101000000-000000000000", + "github.com/aws/smithy-go": "v1.4.0" + }, + "files": [ + "api_client.go", + "api_client_test.go", + "api_op_AddTags.go", + "api_op_ApplySecurityGroupsToLoadBalancer.go", + "api_op_AttachLoadBalancerToSubnets.go", + "api_op_ConfigureHealthCheck.go", + "api_op_CreateAppCookieStickinessPolicy.go", + "api_op_CreateLBCookieStickinessPolicy.go", + "api_op_CreateLoadBalancer.go", + "api_op_CreateLoadBalancerListeners.go", + "api_op_CreateLoadBalancerPolicy.go", + "api_op_DeleteLoadBalancer.go", + "api_op_DeleteLoadBalancerListeners.go", + "api_op_DeleteLoadBalancerPolicy.go", + "api_op_DeregisterInstancesFromLoadBalancer.go", + "api_op_DescribeAccountLimits.go", + "api_op_DescribeInstanceHealth.go", + "api_op_DescribeLoadBalancerAttributes.go", + "api_op_DescribeLoadBalancerPolicies.go", + "api_op_DescribeLoadBalancerPolicyTypes.go", + "api_op_DescribeLoadBalancers.go", + "api_op_DescribeTags.go", + "api_op_DetachLoadBalancerFromSubnets.go", + "api_op_DisableAvailabilityZonesForLoadBalancer.go", + "api_op_EnableAvailabilityZonesForLoadBalancer.go", + "api_op_ModifyLoadBalancerAttributes.go", + "api_op_RegisterInstancesWithLoadBalancer.go", + "api_op_RemoveTags.go", + "api_op_SetLoadBalancerListenerSSLCertificate.go", + "api_op_SetLoadBalancerPoliciesForBackendServer.go", + "api_op_SetLoadBalancerPoliciesOfListener.go", + "auth.go", + "deserializers.go", + "doc.go", + "endpoints.go", + "endpoints_config_test.go", + "endpoints_test.go", + "generated.json", + "internal/endpoints/endpoints.go", + "internal/endpoints/endpoints_test.go", + "options.go", + "request_snapshot_test.go", + "response_snapshot_test.go", + "serializers.go", + "snapshot_test.go", + "sra_operation_order_test.go", + "types/errors.go", + "types/types.go", + "validators.go" + ], + "go": "1.24", + "module": "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing", + "unstable": false +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/go_module_metadata.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/go_module_metadata.go new file mode 100644 index 000000000..e5cc10f82 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/go_module_metadata.go @@ -0,0 +1,6 @@ +// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT. + +package elasticloadbalancing + +// goModuleVersion is the tagged release for this module +const goModuleVersion = "1.36.5" diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/internal/endpoints/endpoints.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/internal/endpoints/endpoints.go new file mode 100644 index 000000000..ed418a875 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/internal/endpoints/endpoints.go @@ -0,0 +1,584 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package endpoints + +import ( + "github.com/aws/aws-sdk-go-v2/aws" + endpoints "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2" + "github.com/aws/smithy-go/logging" + "regexp" +) + +// Options is the endpoint resolver configuration options +type Options struct { + // Logger is a logging implementation that log events should be sent to. + Logger logging.Logger + + // LogDeprecated indicates that deprecated endpoints should be logged to the + // provided logger. + LogDeprecated bool + + // ResolvedRegion is used to override the region to be resolved, rather then the + // using the value passed to the ResolveEndpoint method. This value is used by the + // SDK to translate regions like fips-us-east-1 or us-east-1-fips to an alternative + // name. You must not set this value directly in your application. + ResolvedRegion string + + // DisableHTTPS informs the resolver to return an endpoint that does not use the + // HTTPS scheme. + DisableHTTPS bool + + // UseDualStackEndpoint specifies the resolver must resolve a dual-stack endpoint. + UseDualStackEndpoint aws.DualStackEndpointState + + // UseFIPSEndpoint specifies the resolver must resolve a FIPS endpoint. + UseFIPSEndpoint aws.FIPSEndpointState +} + +func (o Options) GetResolvedRegion() string { + return o.ResolvedRegion +} + +func (o Options) GetDisableHTTPS() bool { + return o.DisableHTTPS +} + +func (o Options) GetUseDualStackEndpoint() aws.DualStackEndpointState { + return o.UseDualStackEndpoint +} + +func (o Options) GetUseFIPSEndpoint() aws.FIPSEndpointState { + return o.UseFIPSEndpoint +} + +func transformToSharedOptions(options Options) endpoints.Options { + return endpoints.Options{ + Logger: options.Logger, + LogDeprecated: options.LogDeprecated, + ResolvedRegion: options.ResolvedRegion, + DisableHTTPS: options.DisableHTTPS, + UseDualStackEndpoint: options.UseDualStackEndpoint, + UseFIPSEndpoint: options.UseFIPSEndpoint, + } +} + +// Resolver Elastic Load Balancing endpoint resolver +type Resolver struct { + partitions endpoints.Partitions +} + +// ResolveEndpoint resolves the service endpoint for the given region and options +func (r *Resolver) ResolveEndpoint(region string, options Options) (endpoint aws.Endpoint, err error) { + if len(region) == 0 { + return endpoint, &aws.MissingRegionError{} + } + + opt := transformToSharedOptions(options) + return r.partitions.ResolveEndpoint(region, opt) +} + +// New returns a new Resolver +func New() *Resolver { + return &Resolver{ + partitions: defaultPartitions, + } +} + +var partitionRegexp = struct { + Aws *regexp.Regexp + AwsCn *regexp.Regexp + AwsEusc *regexp.Regexp + AwsIso *regexp.Regexp + AwsIsoB *regexp.Regexp + AwsIsoE *regexp.Regexp + AwsIsoF *regexp.Regexp + AwsUsGov *regexp.Regexp +}{ + + Aws: regexp.MustCompile("^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$"), + AwsCn: regexp.MustCompile("^cn\\-\\w+\\-\\d+$"), + AwsEusc: regexp.MustCompile("^eusc\\-(de)\\-\\w+\\-\\d+$"), + AwsIso: regexp.MustCompile("^us\\-iso\\-\\w+\\-\\d+$"), + AwsIsoB: regexp.MustCompile("^us\\-isob\\-\\w+\\-\\d+$"), + AwsIsoE: regexp.MustCompile("^eu\\-isoe\\-\\w+\\-\\d+$"), + AwsIsoF: regexp.MustCompile("^us\\-isof\\-\\w+\\-\\d+$"), + AwsUsGov: regexp.MustCompile("^us\\-gov\\-\\w+\\-\\d+$"), +} + +var defaultPartitions = endpoints.Partitions{ + { + ID: "aws", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.Aws, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "af-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-east-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-northeast-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-northeast-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-northeast-3", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-south-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-3", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-4", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-5", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-6", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ap-southeast-7", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ca-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "ca-west-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-central-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-north-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-south-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-west-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-west-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-west-3", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "fips-us-east-1", + }: endpoints.Endpoint{ + Hostname: "elasticloadbalancing-fips.us-east-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-east-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-east-2", + }: endpoints.Endpoint{ + Hostname: "elasticloadbalancing-fips.us-east-2.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-east-2", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-west-1", + }: endpoints.Endpoint{ + Hostname: "elasticloadbalancing-fips.us-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-west-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-west-2", + }: endpoints.Endpoint{ + Hostname: "elasticloadbalancing-fips.us-west-2.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-west-2", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "il-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "me-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "me-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "mx-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "sa-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.us-east-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-east-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-2", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.us-east-2.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-west-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-west-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.us-west-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-west-2", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-west-2", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.us-west-2.amazonaws.com", + }, + }, + }, + { + ID: "aws-cn", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing.{region}.api.amazonwebservices.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.amazonaws.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.api.amazonwebservices.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.amazonaws.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsCn, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "cn-north-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "cn-northwest-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-eusc", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing.{region}.api.amazonwebservices.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.amazonaws.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.api.amazonwebservices.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.amazonaws.eu", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsEusc, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "eusc-de-east-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-iso", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.c2s.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.c2s.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIso, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "us-iso-east-1", + }: endpoints.Endpoint{ + Protocols: []string{"http", "https"}, + }, + endpoints.EndpointKey{ + Region: "us-iso-west-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-iso-b", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.sc2s.sgov.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.sc2s.sgov.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoB, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "us-isob-east-1", + }: endpoints.Endpoint{ + Protocols: []string{"https"}, + }, + endpoints.EndpointKey{ + Region: "us-isob-west-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-iso-e", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.cloud.adc-e.uk", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.cloud.adc-e.uk", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoE, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "eu-isoe-west-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-iso-f", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.csp.hci.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.csp.hci.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoF, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "us-isof-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-isof-south-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-us-gov", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "elasticloadbalancing-fips.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "elasticloadbalancing.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsUsGov, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "fips-us-gov-east-1", + }: endpoints.Endpoint{ + Hostname: "elasticloadbalancing.us-gov-east-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-east-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "fips-us-gov-west-1", + }: endpoints.Endpoint{ + Hostname: "elasticloadbalancing.us-gov-west-1.amazonaws.com", + CredentialScope: endpoints.CredentialScope{ + Region: "us-gov-west-1", + }, + Deprecated: aws.TrueTernary, + }, + endpoints.EndpointKey{ + Region: "us-gov-east-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-gov-east-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing.us-gov-east-1.amazonaws.com", + }, + endpoints.EndpointKey{ + Region: "us-gov-west-1", + }: endpoints.Endpoint{ + Protocols: []string{"http", "https"}, + }, + endpoints.EndpointKey{ + Region: "us-gov-west-1", + Variant: endpoints.FIPSVariant, + }: { + Hostname: "elasticloadbalancing.us-gov-west-1.amazonaws.com", + Protocols: []string{"http", "https"}, + }, + }, + }, +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/options.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/options.go new file mode 100644 index 000000000..74d6b75ac --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/options.go @@ -0,0 +1,243 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + internalauthsmithy "github.com/aws/aws-sdk-go-v2/internal/auth/smithy" + smithyauth "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/logging" + "github.com/aws/smithy-go/metrics" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net/http" +) + +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +type Options struct { + // Set of options to modify how an operation is invoked. These apply to all + // operations invoked for this client. Use functional options on operation call to + // modify this list for per operation behavior. + APIOptions []func(*middleware.Stack) error + + // The optional application specific identifier appended to the User-Agent header. + AppID string + + // This endpoint will be given as input to an EndpointResolverV2. It is used for + // providing a custom base endpoint that is subject to modifications by the + // processing EndpointResolverV2. + BaseEndpoint *string + + // Configures the events that will be sent to the configured logger. + ClientLogMode aws.ClientLogMode + + // The credentials object to use when signing requests. + Credentials aws.CredentialsProvider + + // The configuration DefaultsMode that the SDK should use when constructing the + // clients initial default settings. + DefaultsMode aws.DefaultsMode + + // Disables SDK clock skew correction. When set, the SDK will not adjust request + // signing timestamps to compensate for clock drift between the client and the + // service. + DisableClockSkewCorrection bool + + // The endpoint options to be used when attempting to resolve an endpoint. + EndpointOptions EndpointResolverOptions + + // The service endpoint resolver. + // + // Deprecated: Deprecated: EndpointResolver and WithEndpointResolver. Providing a + // value for this field will likely prevent you from using any endpoint-related + // service features released after the introduction of EndpointResolverV2 and + // BaseEndpoint. + // + // To migrate an EndpointResolver implementation that uses a custom endpoint, set + // the client option BaseEndpoint instead. + EndpointResolver EndpointResolver + + // Resolves the endpoint used for a particular service operation. + EndpointResolverV2 EndpointResolverV2 + + // Signature Version 4 (SigV4) Signer + HTTPSignerV4 HTTPSignerV4 + + // The logger writer interface to write logging messages to. + Logger logging.Logger + + // The client meter provider. + MeterProvider metrics.MeterProvider + + // The region to send requests to. (Required) + Region string + + // RetryMaxAttempts specifies the maximum number attempts an API client will call + // an operation that fails with a retryable error. A value of 0 is ignored, and + // will not be used to configure the API client created default retryer, or modify + // per operation call's retry max attempts. + // + // If specified in an operation call's functional options with a value that is + // different than the constructed client's Options, the Client's Retryer will be + // wrapped to use the operation's specific RetryMaxAttempts value. + RetryMaxAttempts int + + // RetryMode specifies the retry mode the API client will be created with, if + // Retryer option is not also specified. + // + // When creating a new API Clients this member will only be used if the Retryer + // Options member is nil. This value will be ignored if Retryer is not nil. + // + // Currently does not support per operation call overrides, may in the future. + RetryMode aws.RetryMode + + // Retryer guides how HTTP requests should be retried in case of recoverable + // failures. When nil the API client will use a default retryer. The kind of + // default retry created by the API client can be changed with the RetryMode + // option. + Retryer aws.Retryer + + // The RuntimeEnvironment configuration, only populated if the DefaultsMode is set + // to DefaultsModeAuto and is initialized using config.LoadDefaultConfig . You + // should not populate this structure programmatically, or rely on the values here + // within your applications. + RuntimeEnvironment aws.RuntimeEnvironment + + // The client tracer provider. + TracerProvider tracing.TracerProvider + + // The initial DefaultsMode used when the client options were constructed. If the + // DefaultsMode was set to aws.DefaultsModeAuto this will store what the resolved + // value was at that point in time. + // + // Currently does not support per operation call overrides, may in the future. + resolvedDefaultsMode aws.DefaultsMode + + // The HTTP client to invoke API calls with. Defaults to client's default HTTP + // implementation if nil. + HTTPClient HTTPClient + + // Client registry of operation interceptors. + Interceptors smithyhttp.InterceptorRegistry + + // The auth scheme resolver which determines how to authenticate for each + // operation. + AuthSchemeResolver AuthSchemeResolver + + // The list of auth schemes supported by the client. + AuthSchemes []smithyhttp.AuthScheme + + // Priority list of preferred auth scheme names (e.g. sigv4a). + AuthSchemePreference []string +} + +// Copy creates a clone where the APIOptions list is deep copied. +func (o Options) Copy() Options { + to := o + to.APIOptions = make([]func(*middleware.Stack) error, len(o.APIOptions)) + copy(to.APIOptions, o.APIOptions) + to.Interceptors = o.Interceptors.Copy() + + return to +} + +func (o Options) GetIdentityResolver(schemeID string) smithyauth.IdentityResolver { + if schemeID == "aws.auth#sigv4" { + return getSigV4IdentityResolver(o) + } + if schemeID == "smithy.api#noAuth" { + return &smithyauth.AnonymousIdentityResolver{} + } + return nil +} + +// WithAPIOptions returns a functional option for setting the Client's APIOptions +// option. +func WithAPIOptions(optFns ...func(*middleware.Stack) error) func(*Options) { + return func(o *Options) { + o.APIOptions = append(o.APIOptions, optFns...) + } +} + +// Deprecated: EndpointResolver and WithEndpointResolver. Providing a value for +// this field will likely prevent you from using any endpoint-related service +// features released after the introduction of EndpointResolverV2 and BaseEndpoint. +// +// To migrate an EndpointResolver implementation that uses a custom endpoint, set +// the client option BaseEndpoint instead. +func WithEndpointResolver(v EndpointResolver) func(*Options) { + return func(o *Options) { + o.EndpointResolver = v + } +} + +// WithEndpointResolverV2 returns a functional option for setting the Client's +// EndpointResolverV2 option. +func WithEndpointResolverV2(v EndpointResolverV2) func(*Options) { + return func(o *Options) { + o.EndpointResolverV2 = v + } +} + +func getSigV4IdentityResolver(o Options) smithyauth.IdentityResolver { + if o.Credentials != nil { + return &internalauthsmithy.CredentialsProviderAdapter{Provider: o.Credentials} + } + return nil +} + +// WithSigV4SigningName applies an override to the authentication workflow to +// use the given signing name for SigV4-authenticated operations. +// +// This is an advanced setting. The value here is FINAL, taking precedence over +// the resolved signing name from both auth scheme resolution and endpoint +// resolution. +func WithSigV4SigningName(name string) func(*Options) { + fn := func(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, + ) { + return next.HandleInitialize(awsmiddleware.SetSigningName(ctx, name), in) + } + return func(o *Options) { + o.APIOptions = append(o.APIOptions, func(s *middleware.Stack) error { + return s.Initialize.Add( + middleware.InitializeMiddlewareFunc("withSigV4SigningName", fn), + middleware.Before, + ) + }) + } +} + +// WithSigV4SigningRegion applies an override to the authentication workflow to +// use the given signing region for SigV4-authenticated operations. +// +// This is an advanced setting. The value here is FINAL, taking precedence over +// the resolved signing region from both auth scheme resolution and endpoint +// resolution. +func WithSigV4SigningRegion(region string) func(*Options) { + fn := func(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, + ) { + return next.HandleInitialize(awsmiddleware.SetSigningRegion(ctx, region), in) + } + return func(o *Options) { + o.APIOptions = append(o.APIOptions, func(s *middleware.Stack) error { + return s.Initialize.Add( + middleware.InitializeMiddlewareFunc("withSigV4SigningRegion", fn), + middleware.Before, + ) + }) + } +} + +func ignoreAnonymousAuth(options *Options) { + if aws.IsCredentialsProvider(options.Credentials, (*aws.AnonymousCredentials)(nil)) { + options.Credentials = nil + } +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/serializers.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/serializers.go new file mode 100644 index 000000000..d504bbb1d --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/serializers.go @@ -0,0 +1,3041 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "bytes" + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws/protocol/query" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/encoding/httpbinding" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/tracing" + smithyhttp "github.com/aws/smithy-go/transport/http" + "path" +) + +type awsAwsquery_serializeOpAddTags struct { +} + +func (*awsAwsquery_serializeOpAddTags) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpAddTags) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*AddTagsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("AddTags") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentAddTagsInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpApplySecurityGroupsToLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpApplySecurityGroupsToLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpApplySecurityGroupsToLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ApplySecurityGroupsToLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("ApplySecurityGroupsToLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentApplySecurityGroupsToLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpAttachLoadBalancerToSubnets struct { +} + +func (*awsAwsquery_serializeOpAttachLoadBalancerToSubnets) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpAttachLoadBalancerToSubnets) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*AttachLoadBalancerToSubnetsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("AttachLoadBalancerToSubnets") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentAttachLoadBalancerToSubnetsInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpConfigureHealthCheck struct { +} + +func (*awsAwsquery_serializeOpConfigureHealthCheck) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpConfigureHealthCheck) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ConfigureHealthCheckInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("ConfigureHealthCheck") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentConfigureHealthCheckInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpCreateAppCookieStickinessPolicy struct { +} + +func (*awsAwsquery_serializeOpCreateAppCookieStickinessPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpCreateAppCookieStickinessPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateAppCookieStickinessPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("CreateAppCookieStickinessPolicy") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentCreateAppCookieStickinessPolicyInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpCreateLBCookieStickinessPolicy struct { +} + +func (*awsAwsquery_serializeOpCreateLBCookieStickinessPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpCreateLBCookieStickinessPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateLBCookieStickinessPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("CreateLBCookieStickinessPolicy") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentCreateLBCookieStickinessPolicyInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpCreateLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpCreateLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpCreateLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("CreateLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentCreateLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpCreateLoadBalancerListeners struct { +} + +func (*awsAwsquery_serializeOpCreateLoadBalancerListeners) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpCreateLoadBalancerListeners) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateLoadBalancerListenersInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("CreateLoadBalancerListeners") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentCreateLoadBalancerListenersInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpCreateLoadBalancerPolicy struct { +} + +func (*awsAwsquery_serializeOpCreateLoadBalancerPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpCreateLoadBalancerPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateLoadBalancerPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("CreateLoadBalancerPolicy") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentCreateLoadBalancerPolicyInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDeleteLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpDeleteLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDeleteLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DeleteLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDeleteLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDeleteLoadBalancerListeners struct { +} + +func (*awsAwsquery_serializeOpDeleteLoadBalancerListeners) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDeleteLoadBalancerListeners) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteLoadBalancerListenersInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DeleteLoadBalancerListeners") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDeleteLoadBalancerListenersInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDeleteLoadBalancerPolicy struct { +} + +func (*awsAwsquery_serializeOpDeleteLoadBalancerPolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDeleteLoadBalancerPolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteLoadBalancerPolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DeleteLoadBalancerPolicy") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDeleteLoadBalancerPolicyInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDeregisterInstancesFromLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpDeregisterInstancesFromLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDeregisterInstancesFromLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeregisterInstancesFromLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DeregisterInstancesFromLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDeregisterInstancesFromLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeAccountLimits struct { +} + +func (*awsAwsquery_serializeOpDescribeAccountLimits) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeAccountLimits) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeAccountLimitsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeAccountLimits") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeAccountLimitsInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeInstanceHealth struct { +} + +func (*awsAwsquery_serializeOpDescribeInstanceHealth) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeInstanceHealth) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeInstanceHealthInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeInstanceHealth") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeInstanceHealthInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeLoadBalancerAttributes struct { +} + +func (*awsAwsquery_serializeOpDescribeLoadBalancerAttributes) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeLoadBalancerAttributes) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeLoadBalancerAttributesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeLoadBalancerAttributes") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeLoadBalancerAttributesInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeLoadBalancerPolicies struct { +} + +func (*awsAwsquery_serializeOpDescribeLoadBalancerPolicies) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeLoadBalancerPolicies) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeLoadBalancerPoliciesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeLoadBalancerPolicies") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeLoadBalancerPoliciesInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeLoadBalancerPolicyTypes struct { +} + +func (*awsAwsquery_serializeOpDescribeLoadBalancerPolicyTypes) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeLoadBalancerPolicyTypes) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeLoadBalancerPolicyTypesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeLoadBalancerPolicyTypes") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeLoadBalancerPolicyTypesInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeLoadBalancers struct { +} + +func (*awsAwsquery_serializeOpDescribeLoadBalancers) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeLoadBalancers) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeLoadBalancersInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeLoadBalancers") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeLoadBalancersInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDescribeTags struct { +} + +func (*awsAwsquery_serializeOpDescribeTags) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDescribeTags) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeTagsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DescribeTags") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDescribeTagsInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDetachLoadBalancerFromSubnets struct { +} + +func (*awsAwsquery_serializeOpDetachLoadBalancerFromSubnets) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDetachLoadBalancerFromSubnets) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DetachLoadBalancerFromSubnetsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DetachLoadBalancerFromSubnets") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDetachLoadBalancerFromSubnetsInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpDisableAvailabilityZonesForLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpDisableAvailabilityZonesForLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpDisableAvailabilityZonesForLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DisableAvailabilityZonesForLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("DisableAvailabilityZonesForLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentDisableAvailabilityZonesForLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpEnableAvailabilityZonesForLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpEnableAvailabilityZonesForLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpEnableAvailabilityZonesForLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*EnableAvailabilityZonesForLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("EnableAvailabilityZonesForLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentEnableAvailabilityZonesForLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpModifyLoadBalancerAttributes struct { +} + +func (*awsAwsquery_serializeOpModifyLoadBalancerAttributes) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpModifyLoadBalancerAttributes) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ModifyLoadBalancerAttributesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("ModifyLoadBalancerAttributes") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentModifyLoadBalancerAttributesInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpRegisterInstancesWithLoadBalancer struct { +} + +func (*awsAwsquery_serializeOpRegisterInstancesWithLoadBalancer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpRegisterInstancesWithLoadBalancer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*RegisterInstancesWithLoadBalancerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("RegisterInstancesWithLoadBalancer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentRegisterInstancesWithLoadBalancerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpRemoveTags struct { +} + +func (*awsAwsquery_serializeOpRemoveTags) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpRemoveTags) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*RemoveTagsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("RemoveTags") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentRemoveTagsInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpSetLoadBalancerListenerSSLCertificate struct { +} + +func (*awsAwsquery_serializeOpSetLoadBalancerListenerSSLCertificate) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpSetLoadBalancerListenerSSLCertificate) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*SetLoadBalancerListenerSSLCertificateInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("SetLoadBalancerListenerSSLCertificate") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentSetLoadBalancerListenerSSLCertificateInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpSetLoadBalancerPoliciesForBackendServer struct { +} + +func (*awsAwsquery_serializeOpSetLoadBalancerPoliciesForBackendServer) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpSetLoadBalancerPoliciesForBackendServer) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*SetLoadBalancerPoliciesForBackendServerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("SetLoadBalancerPoliciesForBackendServer") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentSetLoadBalancerPoliciesForBackendServerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} + +type awsAwsquery_serializeOpSetLoadBalancerPoliciesOfListener struct { +} + +func (*awsAwsquery_serializeOpSetLoadBalancerPoliciesOfListener) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsquery_serializeOpSetLoadBalancerPoliciesOfListener) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*SetLoadBalancerPoliciesOfListenerInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-www-form-urlencoded") + + bodyWriter := bytes.NewBuffer(nil) + bodyEncoder := query.NewEncoder(bodyWriter) + body := bodyEncoder.Object() + body.Key("Action").String("SetLoadBalancerPoliciesOfListener") + body.Key("Version").String("2012-06-01") + + if err := awsAwsquery_serializeOpDocumentSetLoadBalancerPoliciesOfListenerInput(input, bodyEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + err = bodyEncoder.Encode() + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(bodyWriter.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsAwsquery_serializeDocumentAccessLog(v *types.AccessLog, value query.Value) error { + object := value.Object() + _ = object + + if v.EmitInterval != nil { + objectKey := object.Key("EmitInterval") + objectKey.Integer(*v.EmitInterval) + } + + { + objectKey := object.Key("Enabled") + objectKey.Boolean(v.Enabled) + } + + if v.S3BucketName != nil { + objectKey := object.Key("S3BucketName") + objectKey.String(*v.S3BucketName) + } + + if v.S3BucketPrefix != nil { + objectKey := object.Key("S3BucketPrefix") + objectKey.String(*v.S3BucketPrefix) + } + + return nil +} + +func awsAwsquery_serializeDocumentAdditionalAttribute(v *types.AdditionalAttribute, value query.Value) error { + object := value.Object() + _ = object + + if v.Key != nil { + objectKey := object.Key("Key") + objectKey.String(*v.Key) + } + + if v.Value != nil { + objectKey := object.Key("Value") + objectKey.String(*v.Value) + } + + return nil +} + +func awsAwsquery_serializeDocumentAdditionalAttributes(v []types.AdditionalAttribute, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + if err := awsAwsquery_serializeDocumentAdditionalAttribute(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsquery_serializeDocumentAvailabilityZones(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentConnectionDraining(v *types.ConnectionDraining, value query.Value) error { + object := value.Object() + _ = object + + { + objectKey := object.Key("Enabled") + objectKey.Boolean(v.Enabled) + } + + if v.Timeout != nil { + objectKey := object.Key("Timeout") + objectKey.Integer(*v.Timeout) + } + + return nil +} + +func awsAwsquery_serializeDocumentConnectionSettings(v *types.ConnectionSettings, value query.Value) error { + object := value.Object() + _ = object + + if v.IdleTimeout != nil { + objectKey := object.Key("IdleTimeout") + objectKey.Integer(*v.IdleTimeout) + } + + return nil +} + +func awsAwsquery_serializeDocumentCrossZoneLoadBalancing(v *types.CrossZoneLoadBalancing, value query.Value) error { + object := value.Object() + _ = object + + { + objectKey := object.Key("Enabled") + objectKey.Boolean(v.Enabled) + } + + return nil +} + +func awsAwsquery_serializeDocumentHealthCheck(v *types.HealthCheck, value query.Value) error { + object := value.Object() + _ = object + + if v.HealthyThreshold != nil { + objectKey := object.Key("HealthyThreshold") + objectKey.Integer(*v.HealthyThreshold) + } + + if v.Interval != nil { + objectKey := object.Key("Interval") + objectKey.Integer(*v.Interval) + } + + if v.Target != nil { + objectKey := object.Key("Target") + objectKey.String(*v.Target) + } + + if v.Timeout != nil { + objectKey := object.Key("Timeout") + objectKey.Integer(*v.Timeout) + } + + if v.UnhealthyThreshold != nil { + objectKey := object.Key("UnhealthyThreshold") + objectKey.Integer(*v.UnhealthyThreshold) + } + + return nil +} + +func awsAwsquery_serializeDocumentInstance(v *types.Instance, value query.Value) error { + object := value.Object() + _ = object + + if v.InstanceId != nil { + objectKey := object.Key("InstanceId") + objectKey.String(*v.InstanceId) + } + + return nil +} + +func awsAwsquery_serializeDocumentInstances(v []types.Instance, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + if err := awsAwsquery_serializeDocumentInstance(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsquery_serializeDocumentListener(v *types.Listener, value query.Value) error { + object := value.Object() + _ = object + + if v.InstancePort != nil { + objectKey := object.Key("InstancePort") + objectKey.Integer(*v.InstancePort) + } + + if v.InstanceProtocol != nil { + objectKey := object.Key("InstanceProtocol") + objectKey.String(*v.InstanceProtocol) + } + + { + objectKey := object.Key("LoadBalancerPort") + objectKey.Integer(v.LoadBalancerPort) + } + + if v.Protocol != nil { + objectKey := object.Key("Protocol") + objectKey.String(*v.Protocol) + } + + if v.SSLCertificateId != nil { + objectKey := object.Key("SSLCertificateId") + objectKey.String(*v.SSLCertificateId) + } + + return nil +} + +func awsAwsquery_serializeDocumentListeners(v []types.Listener, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + if err := awsAwsquery_serializeDocumentListener(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsquery_serializeDocumentLoadBalancerAttributes(v *types.LoadBalancerAttributes, value query.Value) error { + object := value.Object() + _ = object + + if v.AccessLog != nil { + objectKey := object.Key("AccessLog") + if err := awsAwsquery_serializeDocumentAccessLog(v.AccessLog, objectKey); err != nil { + return err + } + } + + if v.AdditionalAttributes != nil { + objectKey := object.Key("AdditionalAttributes") + if err := awsAwsquery_serializeDocumentAdditionalAttributes(v.AdditionalAttributes, objectKey); err != nil { + return err + } + } + + if v.ConnectionDraining != nil { + objectKey := object.Key("ConnectionDraining") + if err := awsAwsquery_serializeDocumentConnectionDraining(v.ConnectionDraining, objectKey); err != nil { + return err + } + } + + if v.ConnectionSettings != nil { + objectKey := object.Key("ConnectionSettings") + if err := awsAwsquery_serializeDocumentConnectionSettings(v.ConnectionSettings, objectKey); err != nil { + return err + } + } + + if v.CrossZoneLoadBalancing != nil { + objectKey := object.Key("CrossZoneLoadBalancing") + if err := awsAwsquery_serializeDocumentCrossZoneLoadBalancing(v.CrossZoneLoadBalancing, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeDocumentLoadBalancerNames(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentLoadBalancerNamesMax20(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentPolicyAttribute(v *types.PolicyAttribute, value query.Value) error { + object := value.Object() + _ = object + + if v.AttributeName != nil { + objectKey := object.Key("AttributeName") + objectKey.String(*v.AttributeName) + } + + if v.AttributeValue != nil { + objectKey := object.Key("AttributeValue") + objectKey.String(*v.AttributeValue) + } + + return nil +} + +func awsAwsquery_serializeDocumentPolicyAttributes(v []types.PolicyAttribute, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + if err := awsAwsquery_serializeDocumentPolicyAttribute(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsquery_serializeDocumentPolicyNames(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentPolicyTypeNames(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentPorts(v []int32, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.Integer(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentSecurityGroups(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentSubnets(v []string, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + av.String(v[i]) + } + return nil +} + +func awsAwsquery_serializeDocumentTag(v *types.Tag, value query.Value) error { + object := value.Object() + _ = object + + if v.Key != nil { + objectKey := object.Key("Key") + objectKey.String(*v.Key) + } + + if v.Value != nil { + objectKey := object.Key("Value") + objectKey.String(*v.Value) + } + + return nil +} + +func awsAwsquery_serializeDocumentTagKeyList(v []types.TagKeyOnly, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + if err := awsAwsquery_serializeDocumentTagKeyOnly(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsquery_serializeDocumentTagKeyOnly(v *types.TagKeyOnly, value query.Value) error { + object := value.Object() + _ = object + + if v.Key != nil { + objectKey := object.Key("Key") + objectKey.String(*v.Key) + } + + return nil +} + +func awsAwsquery_serializeDocumentTagList(v []types.Tag, value query.Value) error { + array := value.Array("member") + + for i := range v { + av := array.Value() + if err := awsAwsquery_serializeDocumentTag(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsquery_serializeOpDocumentAddTagsInput(v *AddTagsInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerNames != nil { + objectKey := object.Key("LoadBalancerNames") + if err := awsAwsquery_serializeDocumentLoadBalancerNames(v.LoadBalancerNames, objectKey); err != nil { + return err + } + } + + if v.Tags != nil { + objectKey := object.Key("Tags") + if err := awsAwsquery_serializeDocumentTagList(v.Tags, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentApplySecurityGroupsToLoadBalancerInput(v *ApplySecurityGroupsToLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.SecurityGroups != nil { + objectKey := object.Key("SecurityGroups") + if err := awsAwsquery_serializeDocumentSecurityGroups(v.SecurityGroups, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentAttachLoadBalancerToSubnetsInput(v *AttachLoadBalancerToSubnetsInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.Subnets != nil { + objectKey := object.Key("Subnets") + if err := awsAwsquery_serializeDocumentSubnets(v.Subnets, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentConfigureHealthCheckInput(v *ConfigureHealthCheckInput, value query.Value) error { + object := value.Object() + _ = object + + if v.HealthCheck != nil { + objectKey := object.Key("HealthCheck") + if err := awsAwsquery_serializeDocumentHealthCheck(v.HealthCheck, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentCreateAppCookieStickinessPolicyInput(v *CreateAppCookieStickinessPolicyInput, value query.Value) error { + object := value.Object() + _ = object + + if v.CookieName != nil { + objectKey := object.Key("CookieName") + objectKey.String(*v.CookieName) + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.PolicyName != nil { + objectKey := object.Key("PolicyName") + objectKey.String(*v.PolicyName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentCreateLBCookieStickinessPolicyInput(v *CreateLBCookieStickinessPolicyInput, value query.Value) error { + object := value.Object() + _ = object + + if v.CookieExpirationPeriod != nil { + objectKey := object.Key("CookieExpirationPeriod") + objectKey.Long(*v.CookieExpirationPeriod) + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.PolicyName != nil { + objectKey := object.Key("PolicyName") + objectKey.String(*v.PolicyName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentCreateLoadBalancerInput(v *CreateLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.AvailabilityZones != nil { + objectKey := object.Key("AvailabilityZones") + if err := awsAwsquery_serializeDocumentAvailabilityZones(v.AvailabilityZones, objectKey); err != nil { + return err + } + } + + if v.Listeners != nil { + objectKey := object.Key("Listeners") + if err := awsAwsquery_serializeDocumentListeners(v.Listeners, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.Scheme != nil { + objectKey := object.Key("Scheme") + objectKey.String(*v.Scheme) + } + + if v.SecurityGroups != nil { + objectKey := object.Key("SecurityGroups") + if err := awsAwsquery_serializeDocumentSecurityGroups(v.SecurityGroups, objectKey); err != nil { + return err + } + } + + if v.Subnets != nil { + objectKey := object.Key("Subnets") + if err := awsAwsquery_serializeDocumentSubnets(v.Subnets, objectKey); err != nil { + return err + } + } + + if v.Tags != nil { + objectKey := object.Key("Tags") + if err := awsAwsquery_serializeDocumentTagList(v.Tags, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentCreateLoadBalancerListenersInput(v *CreateLoadBalancerListenersInput, value query.Value) error { + object := value.Object() + _ = object + + if v.Listeners != nil { + objectKey := object.Key("Listeners") + if err := awsAwsquery_serializeDocumentListeners(v.Listeners, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentCreateLoadBalancerPolicyInput(v *CreateLoadBalancerPolicyInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.PolicyAttributes != nil { + objectKey := object.Key("PolicyAttributes") + if err := awsAwsquery_serializeDocumentPolicyAttributes(v.PolicyAttributes, objectKey); err != nil { + return err + } + } + + if v.PolicyName != nil { + objectKey := object.Key("PolicyName") + objectKey.String(*v.PolicyName) + } + + if v.PolicyTypeName != nil { + objectKey := object.Key("PolicyTypeName") + objectKey.String(*v.PolicyTypeName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDeleteLoadBalancerInput(v *DeleteLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDeleteLoadBalancerListenersInput(v *DeleteLoadBalancerListenersInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.LoadBalancerPorts != nil { + objectKey := object.Key("LoadBalancerPorts") + if err := awsAwsquery_serializeDocumentPorts(v.LoadBalancerPorts, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDeleteLoadBalancerPolicyInput(v *DeleteLoadBalancerPolicyInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.PolicyName != nil { + objectKey := object.Key("PolicyName") + objectKey.String(*v.PolicyName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDeregisterInstancesFromLoadBalancerInput(v *DeregisterInstancesFromLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.Instances != nil { + objectKey := object.Key("Instances") + if err := awsAwsquery_serializeDocumentInstances(v.Instances, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeAccountLimitsInput(v *DescribeAccountLimitsInput, value query.Value) error { + object := value.Object() + _ = object + + if v.Marker != nil { + objectKey := object.Key("Marker") + objectKey.String(*v.Marker) + } + + if v.PageSize != nil { + objectKey := object.Key("PageSize") + objectKey.Integer(*v.PageSize) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeInstanceHealthInput(v *DescribeInstanceHealthInput, value query.Value) error { + object := value.Object() + _ = object + + if v.Instances != nil { + objectKey := object.Key("Instances") + if err := awsAwsquery_serializeDocumentInstances(v.Instances, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeLoadBalancerAttributesInput(v *DescribeLoadBalancerAttributesInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeLoadBalancerPoliciesInput(v *DescribeLoadBalancerPoliciesInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.PolicyNames != nil { + objectKey := object.Key("PolicyNames") + if err := awsAwsquery_serializeDocumentPolicyNames(v.PolicyNames, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeLoadBalancerPolicyTypesInput(v *DescribeLoadBalancerPolicyTypesInput, value query.Value) error { + object := value.Object() + _ = object + + if v.PolicyTypeNames != nil { + objectKey := object.Key("PolicyTypeNames") + if err := awsAwsquery_serializeDocumentPolicyTypeNames(v.PolicyTypeNames, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeLoadBalancersInput(v *DescribeLoadBalancersInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerNames != nil { + objectKey := object.Key("LoadBalancerNames") + if err := awsAwsquery_serializeDocumentLoadBalancerNames(v.LoadBalancerNames, objectKey); err != nil { + return err + } + } + + if v.Marker != nil { + objectKey := object.Key("Marker") + objectKey.String(*v.Marker) + } + + if v.PageSize != nil { + objectKey := object.Key("PageSize") + objectKey.Integer(*v.PageSize) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDescribeTagsInput(v *DescribeTagsInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerNames != nil { + objectKey := object.Key("LoadBalancerNames") + if err := awsAwsquery_serializeDocumentLoadBalancerNamesMax20(v.LoadBalancerNames, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDetachLoadBalancerFromSubnetsInput(v *DetachLoadBalancerFromSubnetsInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.Subnets != nil { + objectKey := object.Key("Subnets") + if err := awsAwsquery_serializeDocumentSubnets(v.Subnets, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentDisableAvailabilityZonesForLoadBalancerInput(v *DisableAvailabilityZonesForLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.AvailabilityZones != nil { + objectKey := object.Key("AvailabilityZones") + if err := awsAwsquery_serializeDocumentAvailabilityZones(v.AvailabilityZones, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentEnableAvailabilityZonesForLoadBalancerInput(v *EnableAvailabilityZonesForLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.AvailabilityZones != nil { + objectKey := object.Key("AvailabilityZones") + if err := awsAwsquery_serializeDocumentAvailabilityZones(v.AvailabilityZones, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentModifyLoadBalancerAttributesInput(v *ModifyLoadBalancerAttributesInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerAttributes != nil { + objectKey := object.Key("LoadBalancerAttributes") + if err := awsAwsquery_serializeDocumentLoadBalancerAttributes(v.LoadBalancerAttributes, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentRegisterInstancesWithLoadBalancerInput(v *RegisterInstancesWithLoadBalancerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.Instances != nil { + objectKey := object.Key("Instances") + if err := awsAwsquery_serializeDocumentInstances(v.Instances, objectKey); err != nil { + return err + } + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentRemoveTagsInput(v *RemoveTagsInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerNames != nil { + objectKey := object.Key("LoadBalancerNames") + if err := awsAwsquery_serializeDocumentLoadBalancerNames(v.LoadBalancerNames, objectKey); err != nil { + return err + } + } + + if v.Tags != nil { + objectKey := object.Key("Tags") + if err := awsAwsquery_serializeDocumentTagKeyList(v.Tags, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentSetLoadBalancerListenerSSLCertificateInput(v *SetLoadBalancerListenerSSLCertificateInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + { + objectKey := object.Key("LoadBalancerPort") + objectKey.Integer(v.LoadBalancerPort) + } + + if v.SSLCertificateId != nil { + objectKey := object.Key("SSLCertificateId") + objectKey.String(*v.SSLCertificateId) + } + + return nil +} + +func awsAwsquery_serializeOpDocumentSetLoadBalancerPoliciesForBackendServerInput(v *SetLoadBalancerPoliciesForBackendServerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.InstancePort != nil { + objectKey := object.Key("InstancePort") + objectKey.Integer(*v.InstancePort) + } + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + if v.PolicyNames != nil { + objectKey := object.Key("PolicyNames") + if err := awsAwsquery_serializeDocumentPolicyNames(v.PolicyNames, objectKey); err != nil { + return err + } + } + + return nil +} + +func awsAwsquery_serializeOpDocumentSetLoadBalancerPoliciesOfListenerInput(v *SetLoadBalancerPoliciesOfListenerInput, value query.Value) error { + object := value.Object() + _ = object + + if v.LoadBalancerName != nil { + objectKey := object.Key("LoadBalancerName") + objectKey.String(*v.LoadBalancerName) + } + + { + objectKey := object.Key("LoadBalancerPort") + objectKey.Integer(v.LoadBalancerPort) + } + + if v.PolicyNames != nil { + objectKey := object.Key("PolicyNames") + if err := awsAwsquery_serializeDocumentPolicyNames(v.PolicyNames, objectKey); err != nil { + return err + } + } + + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/errors.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/errors.go new file mode 100644 index 000000000..d0c82d4db --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/errors.go @@ -0,0 +1,591 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +import ( + "fmt" + smithy "github.com/aws/smithy-go" +) + +// The specified load balancer does not exist. +type AccessPointNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *AccessPointNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *AccessPointNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *AccessPointNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "LoadBalancerNotFound" + } + return *e.ErrorCodeOverride +} +func (e *AccessPointNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The specified ARN does not refer to a valid SSL certificate in AWS Identity and +// Access Management (IAM) or AWS Certificate Manager (ACM). Note that if you +// recently uploaded the certificate to IAM, this error might indicate that the +// certificate is not fully available yet. +type CertificateNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *CertificateNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *CertificateNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *CertificateNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "CertificateNotFound" + } + return *e.ErrorCodeOverride +} +func (e *CertificateNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// A request made by Elastic Load Balancing to another service exceeds the maximum +// request rate permitted for your account. +type DependencyThrottleException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *DependencyThrottleException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *DependencyThrottleException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *DependencyThrottleException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "DependencyThrottle" + } + return *e.ErrorCodeOverride +} +func (e *DependencyThrottleException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The specified load balancer name already exists for this account. +type DuplicateAccessPointNameException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *DuplicateAccessPointNameException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *DuplicateAccessPointNameException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *DuplicateAccessPointNameException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "DuplicateLoadBalancerName" + } + return *e.ErrorCodeOverride +} +func (e *DuplicateAccessPointNameException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// A listener already exists for the specified load balancer name and port, but +// with a different instance port, protocol, or SSL certificate. +type DuplicateListenerException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *DuplicateListenerException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *DuplicateListenerException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *DuplicateListenerException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "DuplicateListener" + } + return *e.ErrorCodeOverride +} +func (e *DuplicateListenerException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// A policy with the specified name already exists for this load balancer. +type DuplicatePolicyNameException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *DuplicatePolicyNameException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *DuplicatePolicyNameException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *DuplicatePolicyNameException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "DuplicatePolicyName" + } + return *e.ErrorCodeOverride +} +func (e *DuplicatePolicyNameException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// A tag key was specified more than once. +type DuplicateTagKeysException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *DuplicateTagKeysException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *DuplicateTagKeysException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *DuplicateTagKeysException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "DuplicateTagKeys" + } + return *e.ErrorCodeOverride +} +func (e *DuplicateTagKeysException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The requested configuration change is not valid. +type InvalidConfigurationRequestException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidConfigurationRequestException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidConfigurationRequestException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidConfigurationRequestException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidConfigurationRequest" + } + return *e.ErrorCodeOverride +} +func (e *InvalidConfigurationRequestException) ErrorFault() smithy.ErrorFault { + return smithy.FaultClient +} + +// The specified endpoint is not valid. +type InvalidEndPointException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidEndPointException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidEndPointException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidEndPointException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidInstance" + } + return *e.ErrorCodeOverride +} +func (e *InvalidEndPointException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The specified value for the schema is not valid. You can only specify a scheme +// for load balancers in a VPC. +type InvalidSchemeException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidSchemeException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidSchemeException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidSchemeException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidScheme" + } + return *e.ErrorCodeOverride +} +func (e *InvalidSchemeException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// One or more of the specified security groups do not exist. +type InvalidSecurityGroupException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidSecurityGroupException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidSecurityGroupException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidSecurityGroupException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidSecurityGroup" + } + return *e.ErrorCodeOverride +} +func (e *InvalidSecurityGroupException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The specified VPC has no associated Internet gateway. +type InvalidSubnetException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidSubnetException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidSubnetException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidSubnetException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidSubnet" + } + return *e.ErrorCodeOverride +} +func (e *InvalidSubnetException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The load balancer does not have a listener configured at the specified port. +type ListenerNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *ListenerNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ListenerNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ListenerNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ListenerNotFound" + } + return *e.ErrorCodeOverride +} +func (e *ListenerNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The specified load balancer attribute does not exist. +type LoadBalancerAttributeNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *LoadBalancerAttributeNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *LoadBalancerAttributeNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *LoadBalancerAttributeNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "LoadBalancerAttributeNotFound" + } + return *e.ErrorCodeOverride +} +func (e *LoadBalancerAttributeNotFoundException) ErrorFault() smithy.ErrorFault { + return smithy.FaultClient +} + +// This operation is not allowed. +type OperationNotPermittedException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *OperationNotPermittedException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *OperationNotPermittedException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *OperationNotPermittedException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "OperationNotPermitted" + } + return *e.ErrorCodeOverride +} +func (e *OperationNotPermittedException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// One or more of the specified policies do not exist. +type PolicyNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *PolicyNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *PolicyNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *PolicyNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "PolicyNotFound" + } + return *e.ErrorCodeOverride +} +func (e *PolicyNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// One or more of the specified policy types do not exist. +type PolicyTypeNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *PolicyTypeNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *PolicyTypeNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *PolicyTypeNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "PolicyTypeNotFound" + } + return *e.ErrorCodeOverride +} +func (e *PolicyTypeNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// One or more of the specified subnets do not exist. +type SubnetNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *SubnetNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *SubnetNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *SubnetNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "SubnetNotFound" + } + return *e.ErrorCodeOverride +} +func (e *SubnetNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The quota for the number of load balancers has been reached. +type TooManyAccessPointsException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *TooManyAccessPointsException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *TooManyAccessPointsException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *TooManyAccessPointsException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "TooManyLoadBalancers" + } + return *e.ErrorCodeOverride +} +func (e *TooManyAccessPointsException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The quota for the number of policies for this load balancer has been reached. +type TooManyPoliciesException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *TooManyPoliciesException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *TooManyPoliciesException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *TooManyPoliciesException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "TooManyPolicies" + } + return *e.ErrorCodeOverride +} +func (e *TooManyPoliciesException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The quota for the number of tags that can be assigned to a load balancer has +// been reached. +type TooManyTagsException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *TooManyTagsException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *TooManyTagsException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *TooManyTagsException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "TooManyTags" + } + return *e.ErrorCodeOverride +} +func (e *TooManyTagsException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The specified protocol or signature version is not supported. +type UnsupportedProtocolException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *UnsupportedProtocolException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *UnsupportedProtocolException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *UnsupportedProtocolException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "UnsupportedProtocol" + } + return *e.ErrorCodeOverride +} +func (e *UnsupportedProtocolException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/types.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/types.go new file mode 100644 index 000000000..0c6472247 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types/types.go @@ -0,0 +1,579 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +import ( + smithydocument "github.com/aws/smithy-go/document" + "time" +) + +// Information about the AccessLog attribute. +type AccessLog struct { + + // Specifies whether access logs are enabled for the load balancer. + // + // This member is required. + Enabled bool + + // The interval for publishing the access logs. You can specify an interval of + // either 5 minutes or 60 minutes. + // + // Default: 60 minutes + EmitInterval *int32 + + // The name of the Amazon S3 bucket where the access logs are stored. + S3BucketName *string + + // The logical hierarchy you created for your Amazon S3 bucket, for example + // my-bucket-prefix/prod . If the prefix is not provided, the log is placed at the + // root level of the bucket. + S3BucketPrefix *string + + noSmithyDocumentSerde +} + +// Information about additional load balancer attributes. +type AdditionalAttribute struct { + + // The name of the attribute. + // + // The following attribute is supported. + // + // - elb.http.desyncmitigationmode - Determines how the load balancer handles + // requests that might pose a security risk to your application. The possible + // values are monitor , defensive , and strictest . The default is defensive . + Key *string + + // This value of the attribute. + Value *string + + noSmithyDocumentSerde +} + +// Information about a policy for application-controlled session stickiness. +type AppCookieStickinessPolicy struct { + + // The name of the application cookie used for stickiness. + CookieName *string + + // The mnemonic name for the policy being created. The name must be unique within + // a set of policies for this load balancer. + PolicyName *string + + noSmithyDocumentSerde +} + +// Information about the configuration of an EC2 instance. +type BackendServerDescription struct { + + // The port on which the EC2 instance is listening. + InstancePort *int32 + + // The names of the policies enabled for the EC2 instance. + PolicyNames []string + + noSmithyDocumentSerde +} + +// Information about the ConnectionDraining attribute. +type ConnectionDraining struct { + + // Specifies whether connection draining is enabled for the load balancer. + // + // This member is required. + Enabled bool + + // The maximum time, in seconds, to keep the existing connections open before + // deregistering the instances. + Timeout *int32 + + noSmithyDocumentSerde +} + +// Information about the ConnectionSettings attribute. +type ConnectionSettings struct { + + // The time, in seconds, that the connection is allowed to be idle (no data has + // been sent over the connection) before it is closed by the load balancer. + // + // This member is required. + IdleTimeout *int32 + + noSmithyDocumentSerde +} + +// Information about the CrossZoneLoadBalancing attribute. +type CrossZoneLoadBalancing struct { + + // Specifies whether cross-zone load balancing is enabled for the load balancer. + // + // This member is required. + Enabled bool + + noSmithyDocumentSerde +} + +// Information about a health check. +type HealthCheck struct { + + // The number of consecutive health checks successes required before moving the + // instance to the Healthy state. + // + // This member is required. + HealthyThreshold *int32 + + // The approximate interval, in seconds, between health checks of an individual + // instance. + // + // This member is required. + Interval *int32 + + // The instance being checked. The protocol is either TCP, HTTP, HTTPS, or SSL. + // The range of valid ports is one (1) through 65535. + // + // TCP is the default, specified as a TCP: port pair, for example "TCP:5000". In + // this case, a health check simply attempts to open a TCP connection to the + // instance on the specified port. Failure to connect within the configured timeout + // is considered unhealthy. + // + // SSL is also specified as SSL: port pair, for example, SSL:5000. + // + // For HTTP/HTTPS, you must include a ping path in the string. HTTP is specified + // as a HTTP:port;/;PathToPing; grouping, for example + // "HTTP:80/weather/us/wa/seattle". In this case, a HTTP GET request is issued to + // the instance on the given port and path. Any answer other than "200 OK" within + // the timeout period is considered unhealthy. + // + // The total length of the HTTP ping target must be 1024 16-bit Unicode characters + // or less. + // + // This member is required. + Target *string + + // The amount of time, in seconds, during which no response means a failed health + // check. + // + // This value must be less than the Interval value. + // + // This member is required. + Timeout *int32 + + // The number of consecutive health check failures required before moving the + // instance to the Unhealthy state. + // + // This member is required. + UnhealthyThreshold *int32 + + noSmithyDocumentSerde +} + +// The ID of an EC2 instance. +type Instance struct { + + // The instance ID. + InstanceId *string + + noSmithyDocumentSerde +} + +// Information about the state of an EC2 instance. +type InstanceState struct { + + // A description of the instance state. This string can contain one or more of the + // following messages. + // + // - N/A + // + // - A transient error occurred. Please try again later. + // + // - Instance has failed at least the UnhealthyThreshold number of health checks + // consecutively. + // + // - Instance has not passed the configured HealthyThreshold number of health + // checks consecutively. + // + // - Instance registration is still in progress. + // + // - Instance is in the EC2 Availability Zone for which LoadBalancer is not + // configured to route traffic to. + // + // - Instance is not currently registered with the LoadBalancer. + // + // - Instance deregistration currently in progress. + // + // - Disable Availability Zone is currently in progress. + // + // - Instance is in pending state. + // + // - Instance is in stopped state. + // + // - Instance is in terminated state. + Description *string + + // The ID of the instance. + InstanceId *string + + // Information about the cause of OutOfService instances. Specifically, whether + // the cause is Elastic Load Balancing or the instance. + // + // Valid values: ELB | Instance | N/A + ReasonCode *string + + // The current state of the instance. + // + // Valid values: InService | OutOfService | Unknown + State *string + + noSmithyDocumentSerde +} + +// Information about a policy for duration-based session stickiness. +type LBCookieStickinessPolicy struct { + + // The time period, in seconds, after which the cookie should be considered stale. + // If this parameter is not specified, the stickiness session lasts for the + // duration of the browser session. + CookieExpirationPeriod *int64 + + // The name of the policy. This name must be unique within the set of policies for + // this load balancer. + PolicyName *string + + noSmithyDocumentSerde +} + +// Information about an Elastic Load Balancing resource limit for your AWS account. +type Limit struct { + + // The maximum value of the limit. + Max *string + + // The name of the limit. The possible values are: + // + // - classic-listeners + // + // - classic-load-balancers + // + // - classic-registered-instances + Name *string + + noSmithyDocumentSerde +} + +// Information about a listener. +// +// For information about the protocols and the ports supported by Elastic Load +// Balancing, see [Listeners for Your Classic Load Balancer]in the Classic Load Balancers Guide. +// +// [Listeners for Your Classic Load Balancer]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html +type Listener struct { + + // The port on which the instance is listening. + // + // This member is required. + InstancePort *int32 + + // The port on which the load balancer is listening. On EC2-VPC, you can specify + // any port from the range 1-65535. On EC2-Classic, you can specify any port from + // the following list: 25, 80, 443, 465, 587, 1024-65535. + // + // This member is required. + LoadBalancerPort int32 + + // The load balancer transport protocol to use for routing: HTTP, HTTPS, TCP, or + // SSL. + // + // This member is required. + Protocol *string + + // The protocol to use for routing traffic to instances: HTTP, HTTPS, TCP, or SSL. + // + // If the front-end protocol is TCP or SSL, the back-end protocol must be TCP or + // SSL. If the front-end protocol is HTTP or HTTPS, the back-end protocol must be + // HTTP or HTTPS. + // + // If there is another listener with the same InstancePort whose InstanceProtocol + // is secure, (HTTPS or SSL), the listener's InstanceProtocol must also be secure. + // + // If there is another listener with the same InstancePort whose InstanceProtocol + // is HTTP or TCP, the listener's InstanceProtocol must be HTTP or TCP. + InstanceProtocol *string + + // The Amazon Resource Name (ARN) of the server certificate. + SSLCertificateId *string + + noSmithyDocumentSerde +} + +// The policies enabled for a listener. +type ListenerDescription struct { + + // The listener. + Listener *Listener + + // The policies. If there are no policies enabled, the list is empty. + PolicyNames []string + + noSmithyDocumentSerde +} + +// The attributes for a load balancer. +type LoadBalancerAttributes struct { + + // If enabled, the load balancer captures detailed information of all requests and + // delivers the information to the Amazon S3 bucket that you specify. + // + // For more information, see [Enable Access Logs] in the Classic Load Balancers Guide. + // + // [Enable Access Logs]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-access-logs.html + AccessLog *AccessLog + + // Any additional attributes. + AdditionalAttributes []AdditionalAttribute + + // If enabled, the load balancer allows existing requests to complete before the + // load balancer shifts traffic away from a deregistered or unhealthy instance. + // + // For more information, see [Configure Connection Draining] in the Classic Load Balancers Guide. + // + // [Configure Connection Draining]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-conn-drain.html + ConnectionDraining *ConnectionDraining + + // If enabled, the load balancer allows the connections to remain idle (no data is + // sent over the connection) for the specified duration. + // + // By default, Elastic Load Balancing maintains a 60-second idle connection + // timeout for both front-end and back-end connections of your load balancer. For + // more information, see [Configure Idle Connection Timeout]in the Classic Load Balancers Guide. + // + // [Configure Idle Connection Timeout]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-idle-timeout.html + ConnectionSettings *ConnectionSettings + + // If enabled, the load balancer routes the request traffic evenly across all + // instances regardless of the Availability Zones. + // + // For more information, see [Configure Cross-Zone Load Balancing] in the Classic Load Balancers Guide. + // + // [Configure Cross-Zone Load Balancing]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-disable-crosszone-lb.html + CrossZoneLoadBalancing *CrossZoneLoadBalancing + + noSmithyDocumentSerde +} + +// Information about a load balancer. +type LoadBalancerDescription struct { + + // The Availability Zones for the load balancer. + AvailabilityZones []string + + // Information about your EC2 instances. + BackendServerDescriptions []BackendServerDescription + + // The DNS name of the load balancer. + // + // For more information, see [Configure a Custom Domain Name] in the Classic Load Balancers Guide. + // + // [Configure a Custom Domain Name]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/using-domain-names-with-elb.html + CanonicalHostedZoneName *string + + // The ID of the Amazon Route 53 hosted zone for the load balancer. + CanonicalHostedZoneNameID *string + + // The date and time the load balancer was created. + CreatedTime *time.Time + + // The DNS name of the load balancer. + DNSName *string + + // Information about the health checks conducted on the load balancer. + HealthCheck *HealthCheck + + // The IDs of the instances for the load balancer. + Instances []Instance + + // The listeners for the load balancer. + ListenerDescriptions []ListenerDescription + + // The name of the load balancer. + LoadBalancerName *string + + // The policies defined for the load balancer. + Policies *Policies + + // The type of load balancer. Valid only for load balancers in a VPC. + // + // If Scheme is internet-facing , the load balancer has a public DNS name that + // resolves to a public IP address. + // + // If Scheme is internal , the load balancer has a public DNS name that resolves to + // a private IP address. + Scheme *string + + // The security groups for the load balancer. Valid only for load balancers in a + // VPC. + SecurityGroups []string + + // The security group for the load balancer, which you can use as part of your + // inbound rules for your registered instances. To only allow traffic from load + // balancers, add a security group rule that specifies this source security group + // as the inbound source. + SourceSecurityGroup *SourceSecurityGroup + + // The IDs of the subnets for the load balancer. + Subnets []string + + // The ID of the VPC for the load balancer. + VPCId *string + + noSmithyDocumentSerde +} + +// The policies for a load balancer. +type Policies struct { + + // The stickiness policies created using CreateAppCookieStickinessPolicy. + AppCookieStickinessPolicies []AppCookieStickinessPolicy + + // The stickiness policies created using CreateLBCookieStickinessPolicy. + LBCookieStickinessPolicies []LBCookieStickinessPolicy + + // The policies other than the stickiness policies. + OtherPolicies []string + + noSmithyDocumentSerde +} + +// Information about a policy attribute. +type PolicyAttribute struct { + + // The name of the attribute. + AttributeName *string + + // The value of the attribute. + AttributeValue *string + + noSmithyDocumentSerde +} + +// Information about a policy attribute. +type PolicyAttributeDescription struct { + + // The name of the attribute. + AttributeName *string + + // The value of the attribute. + AttributeValue *string + + noSmithyDocumentSerde +} + +// Information about a policy attribute type. +type PolicyAttributeTypeDescription struct { + + // The name of the attribute. + AttributeName *string + + // The type of the attribute. For example, Boolean or Integer . + AttributeType *string + + // The cardinality of the attribute. + // + // Valid values: + // + // - ONE(1) : Single value required + // + // - ZERO_OR_ONE(0..1) : Up to one value is allowed + // + // - ZERO_OR_MORE(0..*) : Optional. Multiple values are allowed + // + // - ONE_OR_MORE(1..*0) : Required. Multiple values are allowed + Cardinality *string + + // The default value of the attribute, if applicable. + DefaultValue *string + + // A description of the attribute. + Description *string + + noSmithyDocumentSerde +} + +// Information about a policy. +type PolicyDescription struct { + + // The policy attributes. + PolicyAttributeDescriptions []PolicyAttributeDescription + + // The name of the policy. + PolicyName *string + + // The name of the policy type. + PolicyTypeName *string + + noSmithyDocumentSerde +} + +// Information about a policy type. +type PolicyTypeDescription struct { + + // A description of the policy type. + Description *string + + // The description of the policy attributes associated with the policies defined + // by Elastic Load Balancing. + PolicyAttributeTypeDescriptions []PolicyAttributeTypeDescription + + // The name of the policy type. + PolicyTypeName *string + + noSmithyDocumentSerde +} + +// Information about a source security group. +type SourceSecurityGroup struct { + + // The name of the security group. + GroupName *string + + // The owner of the security group. + OwnerAlias *string + + noSmithyDocumentSerde +} + +// Information about a tag. +type Tag struct { + + // The key of the tag. + // + // This member is required. + Key *string + + // The value of the tag. + Value *string + + noSmithyDocumentSerde +} + +// The tags associated with a load balancer. +type TagDescription struct { + + // The name of the load balancer. + LoadBalancerName *string + + // The tags. + Tags []Tag + + noSmithyDocumentSerde +} + +// The key of a tag. +type TagKeyOnly struct { + + // The name of the key. + Key *string + + noSmithyDocumentSerde +} + +type noSmithyDocumentSerde = smithydocument.NoSerde diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/validators.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/validators.go new file mode 100644 index 000000000..8997c82e0 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/validators.go @@ -0,0 +1,1260 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package elasticloadbalancing + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/middleware" +) + +type validateOpAddTags struct { +} + +func (*validateOpAddTags) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpAddTags) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*AddTagsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpAddTagsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpApplySecurityGroupsToLoadBalancer struct { +} + +func (*validateOpApplySecurityGroupsToLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpApplySecurityGroupsToLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ApplySecurityGroupsToLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpApplySecurityGroupsToLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpAttachLoadBalancerToSubnets struct { +} + +func (*validateOpAttachLoadBalancerToSubnets) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpAttachLoadBalancerToSubnets) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*AttachLoadBalancerToSubnetsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpAttachLoadBalancerToSubnetsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpConfigureHealthCheck struct { +} + +func (*validateOpConfigureHealthCheck) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpConfigureHealthCheck) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ConfigureHealthCheckInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpConfigureHealthCheckInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateAppCookieStickinessPolicy struct { +} + +func (*validateOpCreateAppCookieStickinessPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateAppCookieStickinessPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateAppCookieStickinessPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateAppCookieStickinessPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateLBCookieStickinessPolicy struct { +} + +func (*validateOpCreateLBCookieStickinessPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateLBCookieStickinessPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateLBCookieStickinessPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateLBCookieStickinessPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateLoadBalancer struct { +} + +func (*validateOpCreateLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateLoadBalancerListeners struct { +} + +func (*validateOpCreateLoadBalancerListeners) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateLoadBalancerListeners) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateLoadBalancerListenersInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateLoadBalancerListenersInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpCreateLoadBalancerPolicy struct { +} + +func (*validateOpCreateLoadBalancerPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateLoadBalancerPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateLoadBalancerPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateLoadBalancerPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteLoadBalancer struct { +} + +func (*validateOpDeleteLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteLoadBalancerListeners struct { +} + +func (*validateOpDeleteLoadBalancerListeners) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteLoadBalancerListeners) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteLoadBalancerListenersInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteLoadBalancerListenersInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteLoadBalancerPolicy struct { +} + +func (*validateOpDeleteLoadBalancerPolicy) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteLoadBalancerPolicy) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteLoadBalancerPolicyInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteLoadBalancerPolicyInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeregisterInstancesFromLoadBalancer struct { +} + +func (*validateOpDeregisterInstancesFromLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeregisterInstancesFromLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeregisterInstancesFromLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeregisterInstancesFromLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeInstanceHealth struct { +} + +func (*validateOpDescribeInstanceHealth) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeInstanceHealth) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeInstanceHealthInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeInstanceHealthInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeLoadBalancerAttributes struct { +} + +func (*validateOpDescribeLoadBalancerAttributes) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeLoadBalancerAttributes) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeLoadBalancerAttributesInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeLoadBalancerAttributesInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDescribeTags struct { +} + +func (*validateOpDescribeTags) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDescribeTags) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DescribeTagsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDescribeTagsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDetachLoadBalancerFromSubnets struct { +} + +func (*validateOpDetachLoadBalancerFromSubnets) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDetachLoadBalancerFromSubnets) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DetachLoadBalancerFromSubnetsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDetachLoadBalancerFromSubnetsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDisableAvailabilityZonesForLoadBalancer struct { +} + +func (*validateOpDisableAvailabilityZonesForLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDisableAvailabilityZonesForLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DisableAvailabilityZonesForLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDisableAvailabilityZonesForLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpEnableAvailabilityZonesForLoadBalancer struct { +} + +func (*validateOpEnableAvailabilityZonesForLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpEnableAvailabilityZonesForLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*EnableAvailabilityZonesForLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpEnableAvailabilityZonesForLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpModifyLoadBalancerAttributes struct { +} + +func (*validateOpModifyLoadBalancerAttributes) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpModifyLoadBalancerAttributes) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ModifyLoadBalancerAttributesInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpModifyLoadBalancerAttributesInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpRegisterInstancesWithLoadBalancer struct { +} + +func (*validateOpRegisterInstancesWithLoadBalancer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpRegisterInstancesWithLoadBalancer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*RegisterInstancesWithLoadBalancerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpRegisterInstancesWithLoadBalancerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpRemoveTags struct { +} + +func (*validateOpRemoveTags) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpRemoveTags) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*RemoveTagsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpRemoveTagsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpSetLoadBalancerListenerSSLCertificate struct { +} + +func (*validateOpSetLoadBalancerListenerSSLCertificate) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpSetLoadBalancerListenerSSLCertificate) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*SetLoadBalancerListenerSSLCertificateInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpSetLoadBalancerListenerSSLCertificateInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpSetLoadBalancerPoliciesForBackendServer struct { +} + +func (*validateOpSetLoadBalancerPoliciesForBackendServer) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpSetLoadBalancerPoliciesForBackendServer) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*SetLoadBalancerPoliciesForBackendServerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpSetLoadBalancerPoliciesForBackendServerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpSetLoadBalancerPoliciesOfListener struct { +} + +func (*validateOpSetLoadBalancerPoliciesOfListener) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpSetLoadBalancerPoliciesOfListener) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*SetLoadBalancerPoliciesOfListenerInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpSetLoadBalancerPoliciesOfListenerInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +func addOpAddTagsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpAddTags{}, middleware.After) +} + +func addOpApplySecurityGroupsToLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpApplySecurityGroupsToLoadBalancer{}, middleware.After) +} + +func addOpAttachLoadBalancerToSubnetsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpAttachLoadBalancerToSubnets{}, middleware.After) +} + +func addOpConfigureHealthCheckValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpConfigureHealthCheck{}, middleware.After) +} + +func addOpCreateAppCookieStickinessPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateAppCookieStickinessPolicy{}, middleware.After) +} + +func addOpCreateLBCookieStickinessPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateLBCookieStickinessPolicy{}, middleware.After) +} + +func addOpCreateLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateLoadBalancer{}, middleware.After) +} + +func addOpCreateLoadBalancerListenersValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateLoadBalancerListeners{}, middleware.After) +} + +func addOpCreateLoadBalancerPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateLoadBalancerPolicy{}, middleware.After) +} + +func addOpDeleteLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteLoadBalancer{}, middleware.After) +} + +func addOpDeleteLoadBalancerListenersValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteLoadBalancerListeners{}, middleware.After) +} + +func addOpDeleteLoadBalancerPolicyValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteLoadBalancerPolicy{}, middleware.After) +} + +func addOpDeregisterInstancesFromLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeregisterInstancesFromLoadBalancer{}, middleware.After) +} + +func addOpDescribeInstanceHealthValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeInstanceHealth{}, middleware.After) +} + +func addOpDescribeLoadBalancerAttributesValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeLoadBalancerAttributes{}, middleware.After) +} + +func addOpDescribeTagsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDescribeTags{}, middleware.After) +} + +func addOpDetachLoadBalancerFromSubnetsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDetachLoadBalancerFromSubnets{}, middleware.After) +} + +func addOpDisableAvailabilityZonesForLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDisableAvailabilityZonesForLoadBalancer{}, middleware.After) +} + +func addOpEnableAvailabilityZonesForLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpEnableAvailabilityZonesForLoadBalancer{}, middleware.After) +} + +func addOpModifyLoadBalancerAttributesValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpModifyLoadBalancerAttributes{}, middleware.After) +} + +func addOpRegisterInstancesWithLoadBalancerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpRegisterInstancesWithLoadBalancer{}, middleware.After) +} + +func addOpRemoveTagsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpRemoveTags{}, middleware.After) +} + +func addOpSetLoadBalancerListenerSSLCertificateValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpSetLoadBalancerListenerSSLCertificate{}, middleware.After) +} + +func addOpSetLoadBalancerPoliciesForBackendServerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpSetLoadBalancerPoliciesForBackendServer{}, middleware.After) +} + +func addOpSetLoadBalancerPoliciesOfListenerValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpSetLoadBalancerPoliciesOfListener{}, middleware.After) +} + +func validateAccessLog(v *types.AccessLog) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "AccessLog"} + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateConnectionDraining(v *types.ConnectionDraining) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ConnectionDraining"} + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateConnectionSettings(v *types.ConnectionSettings) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ConnectionSettings"} + if v.IdleTimeout == nil { + invalidParams.Add(smithy.NewErrParamRequired("IdleTimeout")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateCrossZoneLoadBalancing(v *types.CrossZoneLoadBalancing) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CrossZoneLoadBalancing"} + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateHealthCheck(v *types.HealthCheck) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "HealthCheck"} + if v.Target == nil { + invalidParams.Add(smithy.NewErrParamRequired("Target")) + } + if v.Interval == nil { + invalidParams.Add(smithy.NewErrParamRequired("Interval")) + } + if v.Timeout == nil { + invalidParams.Add(smithy.NewErrParamRequired("Timeout")) + } + if v.UnhealthyThreshold == nil { + invalidParams.Add(smithy.NewErrParamRequired("UnhealthyThreshold")) + } + if v.HealthyThreshold == nil { + invalidParams.Add(smithy.NewErrParamRequired("HealthyThreshold")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateListener(v *types.Listener) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "Listener"} + if v.Protocol == nil { + invalidParams.Add(smithy.NewErrParamRequired("Protocol")) + } + if v.InstancePort == nil { + invalidParams.Add(smithy.NewErrParamRequired("InstancePort")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateListeners(v []types.Listener) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "Listeners"} + for i := range v { + if err := validateListener(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateLoadBalancerAttributes(v *types.LoadBalancerAttributes) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "LoadBalancerAttributes"} + if v.CrossZoneLoadBalancing != nil { + if err := validateCrossZoneLoadBalancing(v.CrossZoneLoadBalancing); err != nil { + invalidParams.AddNested("CrossZoneLoadBalancing", err.(smithy.InvalidParamsError)) + } + } + if v.AccessLog != nil { + if err := validateAccessLog(v.AccessLog); err != nil { + invalidParams.AddNested("AccessLog", err.(smithy.InvalidParamsError)) + } + } + if v.ConnectionDraining != nil { + if err := validateConnectionDraining(v.ConnectionDraining); err != nil { + invalidParams.AddNested("ConnectionDraining", err.(smithy.InvalidParamsError)) + } + } + if v.ConnectionSettings != nil { + if err := validateConnectionSettings(v.ConnectionSettings); err != nil { + invalidParams.AddNested("ConnectionSettings", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateTag(v *types.Tag) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "Tag"} + if v.Key == nil { + invalidParams.Add(smithy.NewErrParamRequired("Key")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateTagList(v []types.Tag) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "TagList"} + for i := range v { + if err := validateTag(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpAddTagsInput(v *AddTagsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "AddTagsInput"} + if v.LoadBalancerNames == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerNames")) + } + if v.Tags == nil { + invalidParams.Add(smithy.NewErrParamRequired("Tags")) + } else if v.Tags != nil { + if err := validateTagList(v.Tags); err != nil { + invalidParams.AddNested("Tags", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpApplySecurityGroupsToLoadBalancerInput(v *ApplySecurityGroupsToLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ApplySecurityGroupsToLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.SecurityGroups == nil { + invalidParams.Add(smithy.NewErrParamRequired("SecurityGroups")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpAttachLoadBalancerToSubnetsInput(v *AttachLoadBalancerToSubnetsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "AttachLoadBalancerToSubnetsInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.Subnets == nil { + invalidParams.Add(smithy.NewErrParamRequired("Subnets")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpConfigureHealthCheckInput(v *ConfigureHealthCheckInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ConfigureHealthCheckInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.HealthCheck == nil { + invalidParams.Add(smithy.NewErrParamRequired("HealthCheck")) + } else if v.HealthCheck != nil { + if err := validateHealthCheck(v.HealthCheck); err != nil { + invalidParams.AddNested("HealthCheck", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateAppCookieStickinessPolicyInput(v *CreateAppCookieStickinessPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateAppCookieStickinessPolicyInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.PolicyName == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyName")) + } + if v.CookieName == nil { + invalidParams.Add(smithy.NewErrParamRequired("CookieName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateLBCookieStickinessPolicyInput(v *CreateLBCookieStickinessPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateLBCookieStickinessPolicyInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.PolicyName == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateLoadBalancerInput(v *CreateLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.Listeners == nil { + invalidParams.Add(smithy.NewErrParamRequired("Listeners")) + } else if v.Listeners != nil { + if err := validateListeners(v.Listeners); err != nil { + invalidParams.AddNested("Listeners", err.(smithy.InvalidParamsError)) + } + } + if v.Tags != nil { + if err := validateTagList(v.Tags); err != nil { + invalidParams.AddNested("Tags", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateLoadBalancerListenersInput(v *CreateLoadBalancerListenersInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateLoadBalancerListenersInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.Listeners == nil { + invalidParams.Add(smithy.NewErrParamRequired("Listeners")) + } else if v.Listeners != nil { + if err := validateListeners(v.Listeners); err != nil { + invalidParams.AddNested("Listeners", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpCreateLoadBalancerPolicyInput(v *CreateLoadBalancerPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateLoadBalancerPolicyInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.PolicyName == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyName")) + } + if v.PolicyTypeName == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyTypeName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteLoadBalancerInput(v *DeleteLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteLoadBalancerListenersInput(v *DeleteLoadBalancerListenersInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteLoadBalancerListenersInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.LoadBalancerPorts == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerPorts")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteLoadBalancerPolicyInput(v *DeleteLoadBalancerPolicyInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteLoadBalancerPolicyInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.PolicyName == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeregisterInstancesFromLoadBalancerInput(v *DeregisterInstancesFromLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeregisterInstancesFromLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.Instances == nil { + invalidParams.Add(smithy.NewErrParamRequired("Instances")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeInstanceHealthInput(v *DescribeInstanceHealthInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeInstanceHealthInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeLoadBalancerAttributesInput(v *DescribeLoadBalancerAttributesInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeLoadBalancerAttributesInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDescribeTagsInput(v *DescribeTagsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DescribeTagsInput"} + if v.LoadBalancerNames == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerNames")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDetachLoadBalancerFromSubnetsInput(v *DetachLoadBalancerFromSubnetsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DetachLoadBalancerFromSubnetsInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.Subnets == nil { + invalidParams.Add(smithy.NewErrParamRequired("Subnets")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDisableAvailabilityZonesForLoadBalancerInput(v *DisableAvailabilityZonesForLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DisableAvailabilityZonesForLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.AvailabilityZones == nil { + invalidParams.Add(smithy.NewErrParamRequired("AvailabilityZones")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpEnableAvailabilityZonesForLoadBalancerInput(v *EnableAvailabilityZonesForLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "EnableAvailabilityZonesForLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.AvailabilityZones == nil { + invalidParams.Add(smithy.NewErrParamRequired("AvailabilityZones")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpModifyLoadBalancerAttributesInput(v *ModifyLoadBalancerAttributesInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ModifyLoadBalancerAttributesInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.LoadBalancerAttributes == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerAttributes")) + } else if v.LoadBalancerAttributes != nil { + if err := validateLoadBalancerAttributes(v.LoadBalancerAttributes); err != nil { + invalidParams.AddNested("LoadBalancerAttributes", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpRegisterInstancesWithLoadBalancerInput(v *RegisterInstancesWithLoadBalancerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "RegisterInstancesWithLoadBalancerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.Instances == nil { + invalidParams.Add(smithy.NewErrParamRequired("Instances")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpRemoveTagsInput(v *RemoveTagsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "RemoveTagsInput"} + if v.LoadBalancerNames == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerNames")) + } + if v.Tags == nil { + invalidParams.Add(smithy.NewErrParamRequired("Tags")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpSetLoadBalancerListenerSSLCertificateInput(v *SetLoadBalancerListenerSSLCertificateInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "SetLoadBalancerListenerSSLCertificateInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.SSLCertificateId == nil { + invalidParams.Add(smithy.NewErrParamRequired("SSLCertificateId")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpSetLoadBalancerPoliciesForBackendServerInput(v *SetLoadBalancerPoliciesForBackendServerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "SetLoadBalancerPoliciesForBackendServerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.InstancePort == nil { + invalidParams.Add(smithy.NewErrParamRequired("InstancePort")) + } + if v.PolicyNames == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyNames")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpSetLoadBalancerPoliciesOfListenerInput(v *SetLoadBalancerPoliciesOfListenerInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "SetLoadBalancerPoliciesOfListenerInput"} + if v.LoadBalancerName == nil { + invalidParams.Add(smithy.NewErrParamRequired("LoadBalancerName")) + } + if v.PolicyNames == nil { + invalidParams.Add(smithy.NewErrParamRequired("PolicyNames")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/AGENTS.md b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/AGENTS.md index e2a75b8ea..de1e3b2bb 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/AGENTS.md +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/AGENTS.md @@ -68,8 +68,9 @@ cd codegen && ./gradlew build cd codegen && ./gradlew publishToMavenLocal ``` -The codegen artifact version is fixed at `0.1.0` and is not published to -Maven Central — you **MUST** `publishToMavenLocal`. +The codegen artifact version is published to Maven Central and bumped on each +release. For local development against unreleased codegen changes, use +`publishToMavenLocal` and point consumers at `mavenLocal()`. ## Runtime architecture diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/CHANGELOG.md b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/CHANGELOG.md index 2db174e02..ea0448c33 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/CHANGELOG.md +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/CHANGELOG.md @@ -1,3 +1,102 @@ +# Release (2026-08-07) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.7 + * **Bug Fix**: Don't serialize unset JSON documents as `nil` in structure members. + * **Bug Fix**: Fix a deserialization panic around collection members in recursive shape configs. + +# Release (2026-07-31) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.6 + * **Bug Fix**: Fix failure to deserialize any `@httpPayload` struct with a non-string member. + * **Bug Fix**: Fix failure to serialize any `@httpPayload` struct with a nested struct. + +# Release (2026-07-27) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.5 + * **Bug Fix**: Fix a performance issue in awsQuery with large response payloads. + +# Release (2026-07-16) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go/aws-http-auth`: [v1.2.1](aws-http-auth/CHANGELOG.md#v121-2026-07-16) + * **Bug Fix**: Use r.URL.Host when r.Host is unset. +* `github.com/aws/smithy-go/aws-http-auth-schemes`: [v1.0.0](aws-http-auth-schemes/CHANGELOG.md#v100-2026-07-16) + * **Release**: Module `github.com/aws/smithy-go/aws-http-auth-schemes` adds generic smithy-go client support for AWS Sigv4 and Sigv4a. + +# Release (2026-06-26) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.3 + * **Bug Fix**: Fix bug in JSON doc encoder and endpoint host label format validation + +# Release (2026-06-05) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.2 + * **Bug Fix**: Fix incorrect serialization of unions in CBOR-based protocols. + +# Release (2026-06-04) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.1 + * **Bug Fix**: Fixed a deserialization failure in all protocols when encountering a union with explicit null members. + * **Bug Fix**: Fixed a panic when deserializing nested unions in JSON- and CBOR-based protocols. + +# Release (2026-06-02) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.0 + * **Feature**: Add APIs for schema-based serialization. + * **Feature**: Add support for all current AWS and Smithy protocols. + * **Bug Fix**: Enforce max nesting depth of 128 on CBOR payloads. +* `github.com/aws/smithy-go/aws-http-auth`: [v1.2.0](aws-http-auth/CHANGELOG.md#v120-2026-06-02) + * **Feature**: Add event stream signer. + +# Release (2026-05-27) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.26.0 + * **Feature**: Add StringSlice to endpoint rulesfn. + +# Release (2026-04-23) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.25.1 + * **Bug Fix**: Fixed a memory leak in the LRU cache implementation used by some AWS services. + # Release (2026-04-15) ## General Highlights diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/README.md b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/README.md index a413ff3d8..ac5a0a613 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/README.md +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/README.md @@ -8,22 +8,19 @@ The smithy-go runtime requires a minimum version of Go 1.24. **WARNING: All interfaces are subject to change.** -## :no_entry_sign: DO NOT use the code generators in this repository +## :warning: Client codegen is unstable -**The code generators in this repository do not generate working clients at -this time.** +The client code generator in this repository powers the aws-sdk-go-v2. +Arbitrary client generation, while possible, is in an early stage of +development: -In order to generate a usable smithy client you must provide a [protocol definition](https://github.com/aws/smithy-go/blob/main/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ProtocolGenerator.java), -such as [AWS restJson1](https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html), -in order to generate transport mechanisms and serialization/deserialization -code ("serde") accordingly. +* Generated clients are missing certain features that were originally + implemented SDK-side (e.g. retries) +* There may be bugs +* The public APIs of generated clients may be unstable -The code generator does not currently support any protocols out of the box. -Support for all [AWS protocols](https://smithy.io/2.0/aws/protocols/index.html) -exists in [aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2). We are -tracking the movement of those out of the SDK into smithy-go in -[#458](https://github.com/aws/smithy-go/issues/458), but there's currently no -timeline for doing so. +If you are interested in using the client code generators, we encourage you to +experiment and share any feedback with us in an issue. ## Plugins @@ -35,8 +32,6 @@ This repository implements the following Smithy build plugins: | `go-server-codegen` | `software.amazon.smithy.go:smithy-go-codegen` | Implements Go server code generation for Smithy models. | | `go-shape-codegen` | `software.amazon.smithy.go:smithy-go-codegen` | Implements Go shape code generation (types only) for Smithy models. | -**NOTE: Build plugins are not currently published to mavenCentral. You must publish to mavenLocal to make the build plugins visible to the Smithy CLI. The artifact version is currently fixed at 0.1.0.** - ## `go-codegen` ### Configuration @@ -55,9 +50,19 @@ methods and types. The up-to-date list of top-level properties enabled for ### Supported protocols +The protocol a client uses is configured by the `Protocol` field on a client's +`Options`. The SDK will configure a default based on the protocol traits +applied to the modeled service. + | Protocol | Notes | |----------|-------| -| [`smithy.protocols#rpcv2Cbor`](https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html) | Event streaming not yet implemented. | +| [`smithy.protocols#rpcv2Cbor`](https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html) | | +| [`aws.protocols#restJson1`](https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html) | | +| [`aws.protocols#restXml`](https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html) | | +| [`aws.protocols#awsJson1_0`](https://smithy.io/2.0/aws/protocols/aws-json-1_0-protocol.html) | | +| [`aws.protocols#awsJson1_1`](https://smithy.io/2.0/aws/protocols/aws-json-1_1-protocol.html) | | +| [`aws.protocols#awsQuery`](https://smithy.io/2.0/aws/protocols/aws-query-protocol.html) | | +| [`aws.protocols#ec2Query`](https://smithy.io/2.0/aws/protocols/aws-ec2-query-protocol.html) | | ### Example @@ -72,7 +77,7 @@ example created from `smithy init`: ], "maven": { "dependencies": [ - "software.amazon.smithy.go:smithy-go-codegen:0.1.0" + "software.amazon.smithy.go:smithy-go-codegen:[0.1.0,2.0)" ] }, "plugins": { diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/document/document.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/document/document.go index 8f852d95c..82b48eb59 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/document/document.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/document/document.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "strconv" + "time" ) // Marshaler is an interface for a type that marshals a document to its protocol-specific byte representation and @@ -15,26 +16,26 @@ import ( // When defining struct types. the `document` struct tag can be used to control how the value will be // marshaled into the resulting protocol document. // -// // Field is ignored -// Field int `document:"-"` +// // Field is ignored +// Field int `document:"-"` // -// // Field object of key "myName" -// Field int `document:"myName"` +// // Field object of key "myName" +// Field int `document:"myName"` // -// // Field object key of key "myName", and -// // Field is omitted if the field is a zero value for the type. -// Field int `document:"myName,omitempty"` +// // Field object key of key "myName", and +// // Field is omitted if the field is a zero value for the type. +// Field int `document:"myName,omitempty"` // -// // Field object key of "Field", and -// // Field is omitted if the field is a zero value for the type. -// Field int `document:",omitempty"` +// // Field object key of "Field", and +// // Field is omitted if the field is a zero value for the type. +// Field int `document:",omitempty"` // // All struct fields, including anonymous fields, are marshaled unless the // any of the following conditions are meet. // -// - the field is not exported -// - document field tag is "-" -// - document field tag specifies "omitempty", and is a zero value. +// - the field is not exported +// - document field tag is "-" +// - document field tag specifies "omitempty", and is a zero value. // // Pointer and interface values are encoded as the value pointed to or // contained in the interface. A nil value encodes as a null @@ -50,6 +51,13 @@ import ( // // Marshal cannot represent cyclic data structures and will not handle them. // Passing cyclic structures to Marshal will result in an infinite recursion. +// +// Marshaler is not used in schema-serde based services (which are currently +// being rolled out) since having an implementation of Marshaler locks a +// document into support for a specific serial format. Existing implementations +// of Marshaler will continue to encode to JSON as that is effectively the only +// serial format supported for Document prior to the introduction of +// schema-serde. In schema-serde services it is replaced by [Value]. type Marshaler interface { MarshalSmithyDocument() ([]byte, error) } @@ -63,18 +71,94 @@ type Marshaler interface { // // Both generic interface{} and concrete types are valid unmarshal destination types. When unmarshaling a document // into an empty interface the Unmarshaler will store one of these values: -// bool, for boolean values -// document.Number, for arbitrary-precision numbers (int64, float64, big.Int, big.Float) -// string, for string values -// []interface{}, for array values -// map[string]interface{}, for objects -// nil, for null values +// +// bool, for boolean values +// document.Number, for arbitrary-precision numbers (int64, float64, big.Int, big.Float) +// string, for string values +// []interface{}, for array values +// map[string]interface{}, for objects +// nil, for null values // // When unmarshaling, any error that occurs will halt the unmarshal and return the error. type Unmarshaler interface { UnmarshalSmithyDocument(v interface{}) error } +// Value is a sealed type representing a Smithy document value. It covers the +// full Smithy data model including blob and timestamp. +// +// The following types implement Value: +// - [Null] +// - [Boolean] +// - [Number] +// - [String] +// - [Blob] +// - [Timestamp] +// - [List] +// - [Map] +// - [Structure] +// - [Opaque] +type Value interface { + isValue() +} + +// Null is a document null value. +type Null struct{} + +func (Null) isValue() {} + +// Boolean is a document boolean value. +type Boolean bool + +func (Boolean) isValue() {} + +// String is a document string value. +type String string + +func (String) isValue() {} + +// Blob is a document blob value. +type Blob []byte + +func (Blob) isValue() {} + +// Timestamp is a document timestamp value. +type Timestamp time.Time + +func (Timestamp) isValue() {} + +// List is a document list value. +type List []Value + +func (List) isValue() {} + +// Map is a document map value with string keys. +type Map map[string]Value + +func (Map) isValue() {} + +// Structure is a document structure value with an optional discriminator +// identifying the shape it represents. +type Structure struct { + // Discriminator is the absolute shape ID (e.g. + // "com.example#MyShape") of the concrete type this structure + // represents. It may be empty if the type is unknown. + Discriminator string + + // Members maps member names to their document values. + Members map[string]Value +} + +func (Structure) isValue() {} + +// Opaque wraps an arbitrary Go value for backward compatibility with the +// legacy reflection-based document serialization path. +type Opaque struct { + Value any +} + +func (Opaque) isValue() {} + type noSerde interface { noSmithyDocumentSerde() } @@ -96,6 +180,8 @@ func IsNoSerde(x interface{}) bool { // Number is an arbitrary precision numerical value type Number string +func (Number) isValue() {} + // Int64 returns the number as a string. func (n Number) String() string { return string(n) diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/encoding/json/value.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/encoding/json/value.go index b41ff1e15..eac49c44c 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/encoding/json/value.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/encoding/json/value.go @@ -106,6 +106,11 @@ func (jv Value) BigInteger(v *big.Int) { // BigDecimal encodes v as JSON value func (jv Value) BigDecimal(v *big.Float) { + if v.Sign() == 0 && v.Signbit() { + // Preserve negative zero sign which Int64() would lose. + jv.w.Write([]byte("-0")) + return + } if i, accuracy := v.Int64(); accuracy == big.Exact { jv.Long(i) return diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go new file mode 100644 index 000000000..ae0fb7fda --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go @@ -0,0 +1,35 @@ +package bdd + +const resultOffset int32 = 100_000_000 +const intsPerNode = 3 + +// Evaluate traverses a compiled BDD node array and returns the result index. +// nodes is a flat array of [condIdx, hi, lo] triples (1-indexed). +// root is the root node reference. evalCond returns true/false for condition index. +func Evaluate(nodes []int32, root int32, evalCond func(int) bool) int32 { + ref := root + for { + if ref >= resultOffset { + return ref - resultOffset + } + if ref == 1 || ref == -1 { + return 0 // NoMatchRule + } + + complement := ref < 0 + nodeIdx := ref + if complement { + nodeIdx = -ref + } + base := (nodeIdx - 1) * intsPerNode + condIdx := nodes[base] + hi := nodes[base+1] + lo := nodes[base+2] + + if complement != evalCond(int(condIdx)) { + ref = hi + } else { + ref = lo + } + } +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go new file mode 100644 index 000000000..7a82fcd94 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go @@ -0,0 +1,18 @@ +package rulesfn + +// StringSlice is a string slice with a negative-index-aware Get method for use +// in endpoint rule evaluation. +type StringSlice []string + +// Get returns a pointer to the string at index i, or nil if the index is out +// of bounds. Negative indices count from the end of the slice. +func (s StringSlice) Get(i int) *string { + if i < 0 { + i = len(s) + i + } + if i < 0 || i >= len(s) { + return nil + } + v := s[i] + return &v +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go index 0c1154127..68828dbfe 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go @@ -27,6 +27,9 @@ func IsValidHostLabel(input string, allowSubDomains bool) bool { if !smithyhttp.ValidHostLabel(label) { return false } + if label[0] == '-' || label[len(label)-1] == '-' { + return false + } } return true diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/const.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/const.go new file mode 100644 index 000000000..893156c5d --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/const.go @@ -0,0 +1,24 @@ +package eventstream + +// EventStream headers with specific meaning to async API functionality. +const ( + ChunkSignatureHeader = `:chunk-signature` // chunk signature for message + DateHeader = `:date` // Date header for signature + ContentTypeHeader = ":content-type" // message payload content-type + + // Message header and values + MessageTypeHeader = `:message-type` // Identifies type of message. + EventMessageType = `event` + ErrorMessageType = `error` + ExceptionMessageType = `exception` + + // Message Events + EventTypeHeader = `:event-type` // Identifies message event type e.g. "Stats". + + // Message Error + ErrorCodeHeader = `:error-code` + ErrorMessageHeader = `:error-message` + + // Message Exception + ExceptionTypeHeader = `:exception-type` +) diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/debug.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/debug.go new file mode 100644 index 000000000..6049402b1 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/debug.go @@ -0,0 +1,144 @@ +package eventstream + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "strconv" +) + +type decodedMessage struct { + rawMessage + Headers decodedHeaders `json:"headers"` +} +type jsonMessage struct { + Length json.Number `json:"total_length"` + HeadersLen json.Number `json:"headers_length"` + PreludeCRC json.Number `json:"prelude_crc"` + Headers decodedHeaders `json:"headers"` + Payload []byte `json:"payload"` + CRC json.Number `json:"message_crc"` +} + +func (d *decodedMessage) UnmarshalJSON(b []byte) (err error) { + var jsonMsg jsonMessage + if err = json.Unmarshal(b, &jsonMsg); err != nil { + return err + } + + d.Length, err = numAsUint32(jsonMsg.Length) + if err != nil { + return err + } + d.HeadersLen, err = numAsUint32(jsonMsg.HeadersLen) + if err != nil { + return err + } + d.PreludeCRC, err = numAsUint32(jsonMsg.PreludeCRC) + if err != nil { + return err + } + d.Headers = jsonMsg.Headers + d.Payload = jsonMsg.Payload + d.CRC, err = numAsUint32(jsonMsg.CRC) + if err != nil { + return err + } + + return nil +} + +func (d *decodedMessage) MarshalJSON() ([]byte, error) { + jsonMsg := jsonMessage{ + Length: json.Number(strconv.Itoa(int(d.Length))), + HeadersLen: json.Number(strconv.Itoa(int(d.HeadersLen))), + PreludeCRC: json.Number(strconv.Itoa(int(d.PreludeCRC))), + Headers: d.Headers, + Payload: d.Payload, + CRC: json.Number(strconv.Itoa(int(d.CRC))), + } + + return json.Marshal(jsonMsg) +} + +func numAsUint32(n json.Number) (uint32, error) { + v, err := n.Int64() + if err != nil { + return 0, fmt.Errorf("failed to get int64 json number, %v", err) + } + + return uint32(v), nil +} + +func (d decodedMessage) Message() Message { + return Message{ + Headers: Headers(d.Headers), + Payload: d.Payload, + } +} + +type decodedHeaders Headers + +func (hs *decodedHeaders) UnmarshalJSON(b []byte) error { + var jsonHeaders []struct { + Name string `json:"name"` + Type valueType `json:"type"` + Value any `json:"value"` + } + + decoder := json.NewDecoder(bytes.NewReader(b)) + decoder.UseNumber() + if err := decoder.Decode(&jsonHeaders); err != nil { + return err + } + + var headers Headers + for _, h := range jsonHeaders { + value, err := valueFromType(h.Type, h.Value) + if err != nil { + return err + } + headers.Set(h.Name, value) + } + *hs = decodedHeaders(headers) + + return nil +} + +func valueFromType(typ valueType, val any) (Value, error) { + switch typ { + case trueValueType: + return BoolValue(true), nil + case falseValueType: + return BoolValue(false), nil + case int8ValueType: + v, err := val.(json.Number).Int64() + return Int8Value(int8(v)), err + case int16ValueType: + v, err := val.(json.Number).Int64() + return Int16Value(int16(v)), err + case int32ValueType: + v, err := val.(json.Number).Int64() + return Int32Value(int32(v)), err + case int64ValueType: + v, err := val.(json.Number).Int64() + return Int64Value(v), err + case bytesValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + return BytesValue(v), err + case stringValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + return StringValue(string(v)), err + case timestampValueType: + v, err := val.(json.Number).Int64() + return TimestampValue(timeFromEpochMilli(v)), err + case uuidValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + var tv UUIDValue + copy(tv[:], v) + return tv, err + default: + panic(fmt.Sprintf("unknown type, %s, %T", typ.String(), val)) + } +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/decode.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/decode.go new file mode 100644 index 000000000..d9ab7652f --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/decode.go @@ -0,0 +1,218 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/aws/smithy-go/logging" + "hash" + "hash/crc32" + "io" +) + +// DecoderOptions is the Decoder configuration options. +type DecoderOptions struct { + Logger logging.Logger + LogMessages bool +} + +// Decoder provides decoding of an Event Stream messages. +type Decoder struct { + options DecoderOptions +} + +// NewDecoder initializes and returns a Decoder for decoding event +// stream messages from the reader provided. +func NewDecoder(optFns ...func(*DecoderOptions)) *Decoder { + options := DecoderOptions{} + + for _, fn := range optFns { + fn(&options) + } + + return &Decoder{ + options: options, + } +} + +// Decode attempts to decode a single message from the event stream reader. +// Will return the event stream message, or error if decodeMessage fails to read +// the message from the stream. +// +// payloadBuf is a byte slice that will be used in the returned Message.Payload. Callers +// must ensure that the Message.Payload from a previous decode has been consumed before passing in the same underlying +// payloadBuf byte slice. +func (d *Decoder) Decode(reader io.Reader, payloadBuf []byte) (m Message, err error) { + if d.options.Logger != nil && d.options.LogMessages { + debugMsgBuf := bytes.NewBuffer(nil) + reader = io.TeeReader(reader, debugMsgBuf) + defer func() { + logMessageDecode(d.options.Logger, debugMsgBuf, m, err) + }() + } + + m, err = decodeMessage(reader, payloadBuf) + + return m, err +} + +// decodeMessage attempts to decode a single message from the event stream reader. +// Will return the event stream message, or error if decodeMessage fails to read +// the message from the reader. +func decodeMessage(reader io.Reader, payloadBuf []byte) (m Message, err error) { + crc := crc32.New(crc32IEEETable) + hashReader := io.TeeReader(reader, crc) + + prelude, err := decodePrelude(hashReader, crc) + if err != nil { + return Message{}, err + } + + if prelude.HeadersLen > 0 { + lr := io.LimitReader(hashReader, int64(prelude.HeadersLen)) + m.Headers, err = decodeHeaders(lr) + if err != nil { + return Message{}, err + } + } + + if payloadLen := prelude.PayloadLen(); payloadLen > 0 { + buf, err := decodePayload(payloadBuf, io.LimitReader(hashReader, int64(payloadLen))) + if err != nil { + return Message{}, err + } + m.Payload = buf + } + + msgCRC := crc.Sum32() + if err := validateCRC(reader, msgCRC); err != nil { + return Message{}, err + } + + return m, nil +} + +func logMessageDecode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, decodeErr error) { + w := bytes.NewBuffer(nil) + defer func() { logger.Logf(logging.Debug, w.String()) }() + + fmt.Fprintf(w, "Raw message:\n%s\n", + hex.Dump(msgBuf.Bytes())) + + if decodeErr != nil { + fmt.Fprintf(w, "decodeMessage error: %v\n", decodeErr) + return + } + + rawMsg, err := msg.rawMessage() + if err != nil { + fmt.Fprintf(w, "failed to create raw message, %v\n", err) + return + } + + decodedMsg := decodedMessage{ + rawMessage: rawMsg, + Headers: decodedHeaders(msg.Headers), + } + + fmt.Fprintf(w, "Decoded message:\n") + encoder := json.NewEncoder(w) + if err := encoder.Encode(decodedMsg); err != nil { + fmt.Fprintf(w, "failed to generate decoded message, %v\n", err) + } +} + +func decodePrelude(r io.Reader, crc hash.Hash32) (messagePrelude, error) { + var p messagePrelude + + var err error + p.Length, err = decodeUint32(r) + if err != nil { + return messagePrelude{}, err + } + + p.HeadersLen, err = decodeUint32(r) + if err != nil { + return messagePrelude{}, err + } + + if err := p.ValidateLens(); err != nil { + return messagePrelude{}, err + } + + preludeCRC := crc.Sum32() + if err := validateCRC(r, preludeCRC); err != nil { + return messagePrelude{}, err + } + + p.PreludeCRC = preludeCRC + + return p, nil +} + +func decodePayload(buf []byte, r io.Reader) ([]byte, error) { + w := bytes.NewBuffer(buf[0:0]) + + _, err := io.Copy(w, r) + return w.Bytes(), err +} + +func decodeUint8(r io.Reader) (uint8, error) { + type byteReader interface { + ReadByte() (byte, error) + } + + if br, ok := r.(byteReader); ok { + v, err := br.ReadByte() + return v, err + } + + var b [1]byte + _, err := io.ReadFull(r, b[:]) + return b[0], err +} + +func decodeUint16(r io.Reader) (uint16, error) { + var b [2]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint16(bs), nil +} + +func decodeUint32(r io.Reader) (uint32, error) { + var b [4]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint32(bs), nil +} + +func decodeUint64(r io.Reader) (uint64, error) { + var b [8]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint64(bs), nil +} + +func validateCRC(r io.Reader, expect uint32) error { + msgCRC, err := decodeUint32(r) + if err != nil { + return err + } + + if msgCRC != expect { + return ChecksumError{} + } + + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/deserializer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/deserializer.go new file mode 100644 index 000000000..8bc931a32 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/deserializer.go @@ -0,0 +1,294 @@ +package eventstream + +import ( + "fmt" + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeDeserializer wraps a [smithy.ShapeDeserializer] to handle event stream +// message binding traits. +type ShapeDeserializer struct { + Message *Message + + inner smithy.ShapeDeserializer + + depth int + schema *smithy.Schema + + bindings []*smithy.Schema + bindIdx int + inBindings bool + + inBody bool + hasPayload bool + hasBody bool +} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +// NewShapeDeserializer returns a deserializer for a Message. +func NewShapeDeserializer(msg *Message, inner smithy.ShapeDeserializer) *ShapeDeserializer { + return &ShapeDeserializer{ + Message: msg, + inner: inner, + } +} + +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + d.depth++ + if d.depth > 1 { + return d.inner.ReadStruct(s) + } + d.schema = s + for _, m := range s.Members() { + if _, ok := smithy.SchemaTrait[*traits.EventPayload](m); ok { + d.hasPayload = true + } + if isEventBound(m) { + d.bindings = append(d.bindings, m) + } else { + d.hasBody = true + } + } + return nil +} + +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + if d.depth > 1 { + ms, err := d.inner.ReadStructMember() + if ms == nil { + d.depth-- + } + return ms, err + } + + // like httpbinding, throw back the bound stuff first before we drop into + // the body + for d.bindIdx < len(d.bindings) { + m := d.bindings[d.bindIdx] + d.bindIdx++ + if isEventHeader(m) && d.Message.Headers.Get(m.MemberName()) == nil { + continue + } + d.inBindings = true + return m, nil + } + d.inBindings = false + + if d.hasPayload { + d.depth-- + return nil, nil + } + + if !d.hasBody { + d.depth-- + return nil, nil + } + + if !d.inBody { + d.inBody = true + if err := d.inner.ReadStruct(d.schema); err != nil { + return nil, err + } + } + + ms, err := d.inner.ReadStructMember() + if ms == nil { + d.depth-- + } + + return ms, err +} + +func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { + if d.inBindings { + if isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + sv, ok := hv.(StringValue) + if !ok { + return fmt.Errorf("event header %q: expected string, got %T", s.MemberName(), hv) + } + *v = string(sv) + return nil + } + if isEventPayload(s) { + *v = string(d.Message.Payload) + return nil + } + } + return d.inner.ReadString(s, v) +} + +func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { + if d.inBindings && isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + bv, ok := hv.(BoolValue) + if !ok { + return fmt.Errorf("event header %q: expected bool, got %T", s.MemberName(), hv) + } + *v = bool(bv) + return nil + } + return d.inner.ReadBool(s, v) +} + +func (d *ShapeDeserializer) readHeaderInt64(name string) (int64, bool, error) { + hv := d.Message.Headers.Get(name) + if hv == nil { + return 0, false, nil + } + switch v := hv.(type) { + case Int8Value: + return int64(v), true, nil + case Int16Value: + return int64(v), true, nil + case Int32Value: + return int64(v), true, nil + case Int64Value: + return int64(v), true, nil + default: + return 0, false, fmt.Errorf("event header %q: expected integer, got %T", name, hv) + } +} + +type intn interface { + int8 | int16 | int32 | int64 +} + +func readEventHeaderInt[T intn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { + n, ok, err := d.readHeaderInt64(s.MemberName()) + if err != nil || !ok { + return err + } + *v = T(n) + return nil +} + +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt8(s, v) +} + +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt16(s, v) +} + +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt32(s, v) +} + +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt64(s, v) +} + +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + return d.inner.ReadFloat32(s, v) +} + +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + return d.inner.ReadFloat64(s, v) +} + +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + if d.inBindings { + if isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + bv, ok := hv.(BytesValue) + if !ok { + return fmt.Errorf("event header %q: expected bytes, got %T", s.MemberName(), hv) + } + *v = []byte(bv) + return nil + } + if isEventPayload(s) { + *v = d.Message.Payload + return nil + } + } + return d.inner.ReadBlob(s, v) +} + +func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { + if d.inBindings && isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + tv, ok := hv.(TimestampValue) + if !ok { + return fmt.Errorf("event header %q: expected timestamp, got %T", s.MemberName(), hv) + } + *v = time.Time(tv) + return nil + } + return d.inner.ReadTime(s, v) +} + +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + return d.inner.ReadList(s) +} + +func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { + return d.inner.ReadListItem(s) +} + +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + return d.inner.ReadMap(s) +} + +func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { + return d.inner.ReadMapKey(s) +} + +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + return d.inner.ReadUnion(s) +} + +func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { + return d.inner.ReadNil(s) +} + +func (d *ShapeDeserializer) ReadDocument(s *smithy.Schema, v *document.Value) error { + return d.inner.ReadDocument(s, v) +} + +func isEventBound(schema *smithy.Schema) bool { + _, h := smithy.SchemaTrait[*traits.EventHeader](schema) + _, p := smithy.SchemaTrait[*traits.EventPayload](schema) + return h || p +} + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/encode.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/encode.go new file mode 100644 index 000000000..61cf7238d --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/encode.go @@ -0,0 +1,167 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/aws/smithy-go/logging" + "hash" + "hash/crc32" + "io" +) + +// EncoderOptions is the configuration options for Encoder. +type EncoderOptions struct { + Logger logging.Logger + LogMessages bool +} + +// Encoder provides EventStream message encoding. +type Encoder struct { + options EncoderOptions + + headersBuf *bytes.Buffer + messageBuf *bytes.Buffer +} + +// NewEncoder initializes and returns an Encoder to encode Event Stream +// messages. +func NewEncoder(optFns ...func(*EncoderOptions)) *Encoder { + o := EncoderOptions{} + + for _, fn := range optFns { + fn(&o) + } + + return &Encoder{ + options: o, + headersBuf: bytes.NewBuffer(nil), + messageBuf: bytes.NewBuffer(nil), + } +} + +// Encode encodes a single EventStream message to the io.Writer the Encoder +// was created with. An error is returned if writing the message fails. +func (e *Encoder) Encode(w io.Writer, msg Message) (err error) { + e.headersBuf.Reset() + e.messageBuf.Reset() + + var writer io.Writer = e.messageBuf + if e.options.Logger != nil && e.options.LogMessages { + encodeMsgBuf := bytes.NewBuffer(nil) + writer = io.MultiWriter(writer, encodeMsgBuf) + defer func() { + logMessageEncode(e.options.Logger, encodeMsgBuf, msg, err) + }() + } + + if err = EncodeHeaders(e.headersBuf, msg.Headers); err != nil { + return err + } + + crc := crc32.New(crc32IEEETable) + hashWriter := io.MultiWriter(writer, crc) + + headersLen := uint32(e.headersBuf.Len()) + payloadLen := uint32(len(msg.Payload)) + + if err = encodePrelude(hashWriter, crc, headersLen, payloadLen); err != nil { + return err + } + + if headersLen > 0 { + if _, err = io.Copy(hashWriter, e.headersBuf); err != nil { + return err + } + } + + if payloadLen > 0 { + if _, err = hashWriter.Write(msg.Payload); err != nil { + return err + } + } + + msgCRC := crc.Sum32() + if err := binary.Write(writer, binary.BigEndian, msgCRC); err != nil { + return err + } + + _, err = io.Copy(w, e.messageBuf) + + return err +} + +func logMessageEncode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, encodeErr error) { + w := bytes.NewBuffer(nil) + defer func() { logger.Logf(logging.Debug, w.String()) }() + + fmt.Fprintf(w, "Message to encode:\n") + encoder := json.NewEncoder(w) + if err := encoder.Encode(msg); err != nil { + fmt.Fprintf(w, "Failed to get encoded message, %v\n", err) + } + + if encodeErr != nil { + fmt.Fprintf(w, "Encode error: %v\n", encodeErr) + return + } + + fmt.Fprintf(w, "Raw message:\n%s\n", hex.Dump(msgBuf.Bytes())) +} + +func encodePrelude(w io.Writer, crc hash.Hash32, headersLen, payloadLen uint32) error { + p := messagePrelude{ + Length: minMsgLen + headersLen + payloadLen, + HeadersLen: headersLen, + } + if err := p.ValidateLens(); err != nil { + return err + } + + err := binaryWriteFields(w, binary.BigEndian, + p.Length, + p.HeadersLen, + ) + if err != nil { + return err + } + + p.PreludeCRC = crc.Sum32() + err = binary.Write(w, binary.BigEndian, p.PreludeCRC) + if err != nil { + return err + } + + return nil +} + +// EncodeHeaders writes the header values to the writer encoded in the event +// stream format. Returns an error if a header fails to encode. +func EncodeHeaders(w io.Writer, headers Headers) error { + for _, h := range headers { + hn := headerName{ + Len: uint8(len(h.Name)), + } + copy(hn.Name[:hn.Len], h.Name) + if err := hn.encode(w); err != nil { + return err + } + + if err := h.Value.encode(w); err != nil { + return err + } + } + + return nil +} + +func binaryWriteFields(w io.Writer, order binary.ByteOrder, vs ...any) error { + for _, v := range vs { + if err := binary.Write(w, order, v); err != nil { + return err + } + } + return nil +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/error.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/error.go new file mode 100644 index 000000000..7616214dd --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/error.go @@ -0,0 +1,23 @@ +package eventstream + +import "fmt" + +// LengthError provides the error for items being larger than a maximum length. +type LengthError struct { + Part string + Want int + Have int + Value any +} + +func (e LengthError) Error() string { + return fmt.Sprintf("%s length invalid, %d/%d, %v", + e.Part, e.Want, e.Have, e.Value) +} + +// ChecksumError provides the error for message checksum invalidation errors. +type ChecksumError struct{} + +func (e ChecksumError) Error() string { + return "message checksum mismatch" +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header.go new file mode 100644 index 000000000..f580bda4c --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header.go @@ -0,0 +1,175 @@ +package eventstream + +import ( + "encoding/binary" + "fmt" + "io" +) + +// Headers are a collection of EventStream header values. +type Headers []Header + +// Header is a single EventStream Key Value header pair. +type Header struct { + Name string + Value Value +} + +// Set associates the name with a value. If the header name already exists in +// the Headers the value will be replaced with the new one. +func (hs *Headers) Set(name string, value Value) { + var i int + for ; i < len(*hs); i++ { + if (*hs)[i].Name == name { + (*hs)[i].Value = value + return + } + } + + *hs = append(*hs, Header{ + Name: name, Value: value, + }) +} + +// Get returns the Value associated with the header. Nil is returned if the +// value does not exist. +func (hs Headers) Get(name string) Value { + for i := range hs { + if h := hs[i]; h.Name == name { + return h.Value + } + } + return nil +} + +// Del deletes the value in the Headers if it exists. +func (hs *Headers) Del(name string) { + for i := 0; i < len(*hs); i++ { + if (*hs)[i].Name == name { + copy((*hs)[i:], (*hs)[i+1:]) + (*hs) = (*hs)[:len(*hs)-1] + } + } +} + +// Clone returns a deep copy of the headers +func (hs Headers) Clone() Headers { + o := make(Headers, 0, len(hs)) + for _, h := range hs { + o.Set(h.Name, h.Value) + } + return o +} + +func decodeHeaders(r io.Reader) (Headers, error) { + hs := Headers{} + + for { + name, err := decodeHeaderName(r) + if err != nil { + if err == io.EOF { + // EOF while getting header name means no more headers + break + } + return nil, err + } + + value, err := decodeHeaderValue(r) + if err != nil { + return nil, err + } + + hs.Set(name, value) + } + + return hs, nil +} + +func decodeHeaderName(r io.Reader) (string, error) { + var n headerName + + var err error + n.Len, err = decodeUint8(r) + if err != nil { + return "", err + } + + name := n.Name[:n.Len] + if _, err := io.ReadFull(r, name); err != nil { + return "", err + } + + return string(name), nil +} + +func decodeHeaderValue(r io.Reader) (Value, error) { + var raw rawValue + + typ, err := decodeUint8(r) + if err != nil { + return nil, err + } + raw.Type = valueType(typ) + + var v Value + + switch raw.Type { + case trueValueType: + v = BoolValue(true) + case falseValueType: + v = BoolValue(false) + case int8ValueType: + var tv Int8Value + err = tv.decode(r) + v = tv + case int16ValueType: + var tv Int16Value + err = tv.decode(r) + v = tv + case int32ValueType: + var tv Int32Value + err = tv.decode(r) + v = tv + case int64ValueType: + var tv Int64Value + err = tv.decode(r) + v = tv + case bytesValueType: + var tv BytesValue + err = tv.decode(r) + v = tv + case stringValueType: + var tv StringValue + err = tv.decode(r) + v = tv + case timestampValueType: + var tv TimestampValue + err = tv.decode(r) + v = tv + case uuidValueType: + var tv UUIDValue + err = tv.decode(r) + v = tv + default: + panic(fmt.Sprintf("unknown value type %d", raw.Type)) + } + + // Error could be EOF, let caller deal with it + return v, err +} + +const maxHeaderNameLen = 255 + +type headerName struct { + Len uint8 + Name [maxHeaderNameLen]byte +} + +func (v headerName) encode(w io.Writer) error { + if err := binary.Write(w, binary.BigEndian, v.Len); err != nil { + return err + } + + _, err := w.Write(v.Name[:v.Len]) + return err +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header_value.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header_value.go new file mode 100644 index 000000000..61ed35366 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/header_value.go @@ -0,0 +1,521 @@ +package eventstream + +import ( + "encoding/base64" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" +) + +const maxHeaderValueLen = 1<<15 - 1 // 2^15-1 or 32KB - 1 + +// valueType is the EventStream header value type. +type valueType uint8 + +// Header value types +const ( + trueValueType valueType = iota + falseValueType + int8ValueType // Byte + int16ValueType // Short + int32ValueType // Integer + int64ValueType // Long + bytesValueType + stringValueType + timestampValueType + uuidValueType +) + +func (t valueType) String() string { + switch t { + case trueValueType: + return "bool" + case falseValueType: + return "bool" + case int8ValueType: + return "int8" + case int16ValueType: + return "int16" + case int32ValueType: + return "int32" + case int64ValueType: + return "int64" + case bytesValueType: + return "byte_array" + case stringValueType: + return "string" + case timestampValueType: + return "timestamp" + case uuidValueType: + return "uuid" + default: + return fmt.Sprintf("unknown value type %d", uint8(t)) + } +} + +type rawValue struct { + Type valueType + Len uint16 // Only set for variable length slices + Value []byte // byte representation of value, BigEndian encoding. +} + +func (r rawValue) encodeScalar(w io.Writer, v any) error { + return binaryWriteFields(w, binary.BigEndian, + r.Type, + v, + ) +} + +func (r rawValue) encodeFixedSlice(w io.Writer, v []byte) error { + binary.Write(w, binary.BigEndian, r.Type) + + _, err := w.Write(v) + return err +} + +func (r rawValue) encodeBytes(w io.Writer, v []byte) error { + if len(v) > maxHeaderValueLen { + return LengthError{ + Part: "header value", + Want: maxHeaderValueLen, Have: len(v), + Value: v, + } + } + r.Len = uint16(len(v)) + + err := binaryWriteFields(w, binary.BigEndian, + r.Type, + r.Len, + ) + if err != nil { + return err + } + + _, err = w.Write(v) + return err +} + +func (r rawValue) encodeString(w io.Writer, v string) error { + if len(v) > maxHeaderValueLen { + return LengthError{ + Part: "header value", + Want: maxHeaderValueLen, Have: len(v), + Value: v, + } + } + r.Len = uint16(len(v)) + + type stringWriter interface { + WriteString(string) (int, error) + } + + err := binaryWriteFields(w, binary.BigEndian, + r.Type, + r.Len, + ) + if err != nil { + return err + } + + if sw, ok := w.(stringWriter); ok { + _, err = sw.WriteString(v) + } else { + _, err = w.Write([]byte(v)) + } + + return err +} + +func decodeFixedBytesValue(r io.Reader, buf []byte) error { + _, err := io.ReadFull(r, buf) + return err +} + +func decodeBytesValue(r io.Reader) ([]byte, error) { + var raw rawValue + var err error + raw.Len, err = decodeUint16(r) + if err != nil { + return nil, err + } + + buf := make([]byte, raw.Len) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, err + } + + return buf, nil +} + +func decodeStringValue(r io.Reader) (string, error) { + v, err := decodeBytesValue(r) + return string(v), err +} + +// Value represents the abstract header value. +type Value interface { + Get() any + String() string + valueType() valueType + encode(io.Writer) error +} + +// An BoolValue provides eventstream encoding, and representation +// of a Go bool value. +type BoolValue bool + +// Get returns the underlying type +func (v BoolValue) Get() any { + return bool(v) +} + +// valueType returns the EventStream header value type value. +func (v BoolValue) valueType() valueType { + if v { + return trueValueType + } + return falseValueType +} + +func (v BoolValue) String() string { + return strconv.FormatBool(bool(v)) +} + +// encode encodes the BoolValue into an eventstream binary value +// representation. +func (v BoolValue) encode(w io.Writer) error { + return binary.Write(w, binary.BigEndian, v.valueType()) +} + +// An Int8Value provides eventstream encoding, and representation of a Go +// int8 value. +type Int8Value int8 + +// Get returns the underlying value. +func (v Int8Value) Get() any { + return int8(v) +} + +// valueType returns the EventStream header value type value. +func (Int8Value) valueType() valueType { + return int8ValueType +} + +func (v Int8Value) String() string { + return fmt.Sprintf("0x%02x", int8(v)) +} + +// encode encodes the Int8Value into an eventstream binary value +// representation. +func (v Int8Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeScalar(w, v) +} + +func (v *Int8Value) decode(r io.Reader) error { + n, err := decodeUint8(r) + if err != nil { + return err + } + + *v = Int8Value(n) + return nil +} + +// An Int16Value provides eventstream encoding, and representation of a Go +// int16 value. +type Int16Value int16 + +// Get returns the underlying value. +func (v Int16Value) Get() any { + return int16(v) +} + +// valueType returns the EventStream header value type value. +func (Int16Value) valueType() valueType { + return int16ValueType +} + +func (v Int16Value) String() string { + return fmt.Sprintf("0x%04x", int16(v)) +} + +// encode encodes the Int16Value into an eventstream binary value +// representation. +func (v Int16Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int16Value) decode(r io.Reader) error { + n, err := decodeUint16(r) + if err != nil { + return err + } + + *v = Int16Value(n) + return nil +} + +// An Int32Value provides eventstream encoding, and representation of a Go +// int32 value. +type Int32Value int32 + +// Get returns the underlying value. +func (v Int32Value) Get() any { + return int32(v) +} + +// valueType returns the EventStream header value type value. +func (Int32Value) valueType() valueType { + return int32ValueType +} + +func (v Int32Value) String() string { + return fmt.Sprintf("0x%08x", int32(v)) +} + +// encode encodes the Int32Value into an eventstream binary value +// representation. +func (v Int32Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int32Value) decode(r io.Reader) error { + n, err := decodeUint32(r) + if err != nil { + return err + } + + *v = Int32Value(n) + return nil +} + +// An Int64Value provides eventstream encoding, and representation of a Go +// int64 value. +type Int64Value int64 + +// Get returns the underlying value. +func (v Int64Value) Get() any { + return int64(v) +} + +// valueType returns the EventStream header value type value. +func (Int64Value) valueType() valueType { + return int64ValueType +} + +func (v Int64Value) String() string { + return fmt.Sprintf("0x%016x", int64(v)) +} + +// encode encodes the Int64Value into an eventstream binary value +// representation. +func (v Int64Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int64Value) decode(r io.Reader) error { + n, err := decodeUint64(r) + if err != nil { + return err + } + + *v = Int64Value(n) + return nil +} + +// An BytesValue provides eventstream encoding, and representation of a Go +// byte slice. +type BytesValue []byte + +// Get returns the underlying value. +func (v BytesValue) Get() any { + return []byte(v) +} + +// valueType returns the EventStream header value type value. +func (BytesValue) valueType() valueType { + return bytesValueType +} + +func (v BytesValue) String() string { + return base64.StdEncoding.EncodeToString([]byte(v)) +} + +// encode encodes the BytesValue into an eventstream binary value +// representation. +func (v BytesValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeBytes(w, []byte(v)) +} + +func (v *BytesValue) decode(r io.Reader) error { + buf, err := decodeBytesValue(r) + if err != nil { + return err + } + + *v = BytesValue(buf) + return nil +} + +// An StringValue provides eventstream encoding, and representation of a Go +// string. +type StringValue string + +// Get returns the underlying value. +func (v StringValue) Get() any { + return string(v) +} + +// valueType returns the EventStream header value type value. +func (StringValue) valueType() valueType { + return stringValueType +} + +func (v StringValue) String() string { + return string(v) +} + +// encode encodes the StringValue into an eventstream binary value +// representation. +func (v StringValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeString(w, string(v)) +} + +func (v *StringValue) decode(r io.Reader) error { + s, err := decodeStringValue(r) + if err != nil { + return err + } + + *v = StringValue(s) + return nil +} + +// An TimestampValue provides eventstream encoding, and representation of a Go +// timestamp. +type TimestampValue time.Time + +// Get returns the underlying value. +func (v TimestampValue) Get() any { + return time.Time(v) +} + +// valueType returns the EventStream header value type value. +func (TimestampValue) valueType() valueType { + return timestampValueType +} + +func (v TimestampValue) epochMilli() int64 { + nano := time.Time(v).UnixNano() + msec := nano / int64(time.Millisecond) + return msec +} + +func (v TimestampValue) String() string { + msec := v.epochMilli() + return strconv.FormatInt(msec, 10) +} + +// encode encodes the TimestampValue into an eventstream binary value +// representation. +func (v TimestampValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + msec := v.epochMilli() + return raw.encodeScalar(w, msec) +} + +func (v *TimestampValue) decode(r io.Reader) error { + n, err := decodeUint64(r) + if err != nil { + return err + } + + *v = TimestampValue(timeFromEpochMilli(int64(n))) + return nil +} + +// MarshalJSON implements the json.Marshaler interface +func (v TimestampValue) MarshalJSON() ([]byte, error) { + return []byte(v.String()), nil +} + +func timeFromEpochMilli(t int64) time.Time { + secs := t / 1e3 + msec := t % 1e3 + return time.Unix(secs, msec*int64(time.Millisecond)).UTC() +} + +// An UUIDValue provides eventstream encoding, and representation of a UUID +// value. +type UUIDValue [16]byte + +// Get returns the underlying value. +func (v UUIDValue) Get() any { + return v[:] +} + +// valueType returns the EventStream header value type value. +func (UUIDValue) valueType() valueType { + return uuidValueType +} + +func (v UUIDValue) String() string { + var scratch [36]byte + + const dash = '-' + + hex.Encode(scratch[:8], v[0:4]) + scratch[8] = dash + hex.Encode(scratch[9:13], v[4:6]) + scratch[13] = dash + hex.Encode(scratch[14:18], v[6:8]) + scratch[18] = dash + hex.Encode(scratch[19:23], v[8:10]) + scratch[23] = dash + hex.Encode(scratch[24:], v[10:]) + + return string(scratch[:]) +} + +// encode encodes the UUIDValue into an eventstream binary value +// representation. +func (v UUIDValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeFixedSlice(w, v[:]) +} + +func (v *UUIDValue) decode(r io.Reader) error { + tv := (*v)[:] + return decodeFixedBytesValue(r, tv) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/message.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/message.go new file mode 100644 index 000000000..1a77654f7 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/message.go @@ -0,0 +1,99 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "hash/crc32" +) + +const preludeLen = 8 +const preludeCRCLen = 4 +const msgCRCLen = 4 +const minMsgLen = preludeLen + preludeCRCLen + msgCRCLen + +var crc32IEEETable = crc32.MakeTable(crc32.IEEE) + +// A Message provides the eventstream message representation. +type Message struct { + Headers Headers + Payload []byte +} + +func (m *Message) rawMessage() (rawMessage, error) { + var raw rawMessage + + if len(m.Headers) > 0 { + var headers bytes.Buffer + if err := EncodeHeaders(&headers, m.Headers); err != nil { + return rawMessage{}, err + } + raw.Headers = headers.Bytes() + raw.HeadersLen = uint32(len(raw.Headers)) + } + + raw.Length = raw.HeadersLen + uint32(len(m.Payload)) + minMsgLen + + hash := crc32.New(crc32IEEETable) + binaryWriteFields(hash, binary.BigEndian, raw.Length, raw.HeadersLen) + raw.PreludeCRC = hash.Sum32() + + binaryWriteFields(hash, binary.BigEndian, raw.PreludeCRC) + + if raw.HeadersLen > 0 { + hash.Write(raw.Headers) + } + + // Read payload bytes and update hash for it as well. + if len(m.Payload) > 0 { + raw.Payload = m.Payload + hash.Write(raw.Payload) + } + + raw.CRC = hash.Sum32() + + return raw, nil +} + +// Clone returns a deep copy of the message. +func (m Message) Clone() Message { + var payload []byte + if m.Payload != nil { + payload = make([]byte, len(m.Payload)) + copy(payload, m.Payload) + } + + return Message{ + Headers: m.Headers.Clone(), + Payload: payload, + } +} + +type messagePrelude struct { + Length uint32 + HeadersLen uint32 + PreludeCRC uint32 +} + +func (p messagePrelude) PayloadLen() uint32 { + return p.Length - p.HeadersLen - minMsgLen +} + +func (p messagePrelude) ValidateLens() error { + if p.Length == 0 { + return LengthError{ + Part: "message prelude", + Want: minMsgLen, + Have: int(p.Length), + } + } + return nil +} + +type rawMessage struct { + messagePrelude + + Headers []byte + Payload []byte + + CRC uint32 +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/serializer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/serializer.go new file mode 100644 index 000000000..018481e93 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/serializer.go @@ -0,0 +1,228 @@ +package eventstream + +import ( + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeSerializer wraps a [smithy.ShapeSerializer], much like the internal +// httpbinding serializer, to handle event stream message binding traits. +type ShapeSerializer struct { + Message *Message + + inner smithy.ShapeSerializer + contentType string // may be inflenced by bindings + depth int + hasBody bool +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer returns a serializer for a single Message. +func NewShapeSerializer(msg *Message, inner smithy.ShapeSerializer) *ShapeSerializer { + return &ShapeSerializer{ + Message: msg, + inner: inner, + } +} + +// ContentType returns the resolved content type for the event message payload +// after serialization, which may be affected by bindings. +func (s *ShapeSerializer) ContentType() string { + return s.contentType +} + +// Bytes returns the serialized body bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.inner.Bytes() +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), BoolValue(v)) + return + } + s.inner.WriteBool(schema, v) +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int8Value(v)) + return + } + s.inner.WriteInt8(schema, v) +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int16Value(v)) + return + } + s.inner.WriteInt16(schema, v) +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int32Value(v)) + return + } + s.inner.WriteInt32(schema, v) +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int64Value(v)) + return + } + s.inner.WriteInt64(schema, v) +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + s.inner.WriteFloat32(schema, v) +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + s.inner.WriteFloat64(schema, v) +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), StringValue(v)) + return + } + if isEventPayload(schema) { + s.Message.Payload = []byte(v) + s.contentType = "text/plain" + return + } + s.inner.WriteString(schema, v) +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), BytesValue(v)) + return + } + if isEventPayload(schema) { + s.Message.Payload = v + s.contentType = "application/octet-stream" + return + } + s.inner.WriteBlob(schema, v) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), TimestampValue(v)) + return + } + s.inner.WriteTime(schema, v) +} + +// WriteBigInt implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigInt(schema *smithy.Schema, v *big.Int) { + s.inner.WriteBigInt(schema, v) +} + +// WriteBigFloat implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { + s.inner.WriteBigFloat(schema, v) +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { + s.depth++ + if s.depth > 1 { + s.inner.WriteStruct(schema) + return + } + // At depth 1 (the event struct itself), start a JSON body if there are + // implicit body members (members without @eventHeader or @eventPayload). + for _, m := range schema.Members() { + if !isEventBound(m) { + s.inner.WriteStruct(schema) + s.hasBody = true + return + } + } +} + +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { + if s.depth > 1 || s.hasBody { + s.inner.CloseStruct() + } + if s.depth == 1 { + s.hasBody = false + } + s.depth-- +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema) { + s.inner.WriteUnion(schema, variant) +} + +// CloseUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseUnion() { + s.inner.CloseUnion() +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { + s.inner.WriteNil(schema) +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + s.inner.WriteList(schema) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + s.inner.CloseList() +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + s.inner.WriteMap(schema) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { + s.inner.WriteKey(schema, key) +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + s.inner.CloseMap() +} + +// WriteDocument implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + s.inner.WriteDocument(schema, v) +} + +func isEventHeader(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.EventHeader](schema) + return ok +} + +func isEventPayload(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.EventPayload](schema) + return ok +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/signer.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/signer.go new file mode 100644 index 000000000..69f7779d8 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/signer.go @@ -0,0 +1,82 @@ +package eventstream + +import ( + "bytes" + "io" + "time" +) + +// MessageSigner signs event stream message header and payload byte pairs. +// Each invocation chains off the previous signature. +type MessageSigner interface { + SignMessage(headers, payload []byte, signingTime time.Time) ([]byte, error) +} + +// SigningWriter wraps an io.WriteCloser and signs each event stream message +// frame written to it. Each Write call MUST contain exactly one complete +// encoded event stream message frame. +// +// The signing writer wraps each incoming frame in an outer event stream +// message with :date and :chunk-signature headers, then encodes the outer +// message to the underlying writer. +// +// Close sends a signed empty message to signal end-of-stream, then closes +// the underlying writer. +type SigningWriter struct { + writer io.WriteCloser + signer MessageSigner + encoder *Encoder + + headersBuf bytes.Buffer +} + +// NewSigningWriter returns a SigningWriter that signs frames and writes them +// to w. +func NewSigningWriter(w io.WriteCloser, signer MessageSigner) *SigningWriter { + return &SigningWriter{ + writer: w, + signer: signer, + encoder: NewEncoder(), + } +} + +// Write signs a complete event stream message frame and writes the signed +// outer envelope to the underlying writer. +func (s *SigningWriter) Write(frame []byte) (int, error) { + if err := s.signAndWrite(frame); err != nil { + return 0, err + } + return len(frame), nil +} + +// Close sends a signed empty message to signal end-of-stream, then closes +// the underlying writer. +func (s *SigningWriter) Close() error { + if err := s.signAndWrite([]byte{}); err != nil { + _ = s.writer.Close() + return err + } + return s.writer.Close() +} + +func (s *SigningWriter) signAndWrite(payload []byte) error { + now := time.Now().UTC() + + var msg Message + msg.Headers.Set(DateHeader, TimestampValue(now)) + msg.Payload = payload + + s.headersBuf.Reset() + if err := EncodeHeaders(&s.headersBuf, msg.Headers); err != nil { + return err + } + + sig, err := s.signer.SignMessage(s.headersBuf.Bytes(), payload, now) + if err != nil { + return err + } + + msg.Headers.Set(ChunkSignatureHeader, BytesValue(sig)) + + return s.encoder.Encode(s.writer, msg) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/types.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/types.go new file mode 100644 index 000000000..4627bb209 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/eventstream/types.go @@ -0,0 +1,26 @@ +package eventstream + +import "github.com/aws/smithy-go" + +// UnknownUnionMember is returned when a union member is returned over the +// wire, but has an unknown tag. +type UnknownUnionMember struct { + Tag string + Value []byte +} + +// Deserialize is a no-op. The raw bytes are already captured in Value. +func (*UnknownUnionMember) Deserialize(smithy.ShapeDeserializer) error { + return nil +} + +// UnknownMessageError provides an error when a message is received from the +// stream, but the reader is unable to determine what kind of message it is. +type UnknownMessageError struct { + Type string + Message *Message +} + +func (e *UnknownMessageError) Error() string { + return "unknown event stream message type, " + e.Type +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/go_module_metadata.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/go_module_metadata.go index 35938d407..3dca7140b 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/go_module_metadata.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/go_module_metadata.go @@ -3,4 +3,4 @@ package smithy // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.25.0" +const goModuleVersion = "1.27.7" diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema.go new file mode 100644 index 000000000..8e9c209a5 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema.go @@ -0,0 +1,332 @@ +package smithy + +import ( + "fmt" + "strings" + "sync/atomic" + "unsafe" +) + +// ShapeType is a type of Smithy shape. +// See https://smithy.io/2.0/spec/idl.html#defining-shapes. +type ShapeType int + +// Enumerates ShapeType per the Smithy IDL. +const ( + ShapeTypeBlob ShapeType = iota + ShapeTypeBoolean + ShapeTypeString + ShapeTypeTimestamp + ShapeTypeByte + ShapeTypeShort + ShapeTypeInteger + ShapeTypeLong + ShapeTypeFloat + ShapeTypeDocument + ShapeTypeDouble + ShapeTypeBigDecimal + ShapeTypeBigInteger + ShapeTypeEnum + ShapeTypeIntEnum + ShapeTypeList + ShapeTypeSet + ShapeTypeMap + ShapeTypeStructure + ShapeTypeUnion + ShapeTypeMember + ShapeTypeService + ShapeTypeResource + ShapeTypeOperation +) + +// ShapeID fields of a Smithy shape ID. +type ShapeID struct { + Namespace, Name, Member string +} + +// String returns the IDL microformat for the shape ID. +func (s ShapeID) String() string { + if s.Member == "" { + return fmt.Sprintf("%s#%s", s.Namespace, s.Name) + } + return fmt.Sprintf("%s#%s$%s", s.Namespace, s.Name, s.Member) +} + +func stoid(s string) ShapeID { + ns, n, _ := strings.Cut(s, "#") + n, m, _ := strings.Cut(n, "$") + return ShapeID{ns, n, m} +} + +// Schema encodes information about a shape from a Smithy model. +// +// Generated clients use schemas at runtime to dynamically (de)serialize +// request/responses. +type Schema struct { + id ShapeID + typ ShapeType + members map[string]*Schema // member name -> schema + traits map[ShapeID]Trait // trait ID -> non-indexed traits only + indexed []Trait // indexed trait slots, sized to max index present + directMask uint64 // bitmask: bit i set means indexed[i] was declared directly on this schema + targetID ShapeID // for member schemas, the target's shape ID + + // resolved on the fly and cached + listMember atomic.Pointer[Schema] + mapKey, mapValue atomic.Pointer[Schema] + + ext [numExtensionSlots]unsafe.Pointer // lazily-computed codec extensions, accessed atomically +} + +// NewSchema creates a new Schema with the given shape ID and traits. +func NewSchema(id ShapeID, typ ShapeType, numMembers int, ts ...Trait) *Schema { + s := &Schema{ + id: id, + typ: typ, + members: make(map[string]*Schema, numMembers), + } + for _, t := range ts { + s.addTrait(t, true) + } + return s +} + +func (s *Schema) addTrait(t Trait, direct bool) { + if it, ok := t.(IndexableTrait); ok { + idx := it.TraitIndex() + if idx >= len(s.indexed) { + s.indexed = append(s.indexed, make([]Trait, idx-len(s.indexed)+1)...) + } + s.indexed[idx] = t + if direct { + s.directMask |= 1 << uint(idx) + } + return + } + + if s.traits == nil { + s.traits = map[ShapeID]Trait{} + } + s.traits[t.TraitID()] = t +} + +// AddMember adds a member to the schema derived from the target, with +// optional trait overrides. The member schema is returned for caller +// reference. +// +// The member schema's effective trait view (accessed via [SchemaTrait]) +// inherits all of the target's traits, then applies the overrides. The +// member's direct trait view (accessed via [SchemaDirectTrait]) contains +// only the overrides, i.e. the traits declared directly on the member. +func (s *Schema) AddMember(name string, target *Schema, ts ...Trait) *Schema { + m := &Schema{ + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + indexed: cloneIndexed(target.indexed), + traits: cloneTraits(target.traits), + directMask: 0, // inherited traits are not direct + targetID: target.id, + } + + // member-declared traits override and are direct + for _, t := range ts { + m.addTrait(t, true) + } + + s.members[name] = m + + // Invalidate cached extensions, schema structure changed. + for i := range s.ext { + atomic.StorePointer(&s.ext[i], nil) + } + + return m +} + +func cloneIndexed(src []Trait) []Trait { + if src == nil { + return nil + } + dst := make([]Trait, len(src)) + copy(dst, src) + return dst +} + +func cloneTraits(src map[ShapeID]Trait) map[ShapeID]Trait { + if src == nil { + return nil + } + dst := make(map[ShapeID]Trait, len(src)) + for k, v := range src { + dst[k] = v + } + return dst +} + +// ListMember returns the "member" schema for list types. +func (s *Schema) ListMember() *Schema { + return s.lookup(&s.listMember, "member") +} + +// MapKey returns the "key" schema for map types. +func (s *Schema) MapKey() *Schema { + return s.lookup(&s.mapKey, "key") +} + +// MapValue returns the "value" schema for map types. +func (s *Schema) MapValue() *Schema { + return s.lookup(&s.mapValue, "value") +} + +func (s *Schema) lookup(cached *atomic.Pointer[Schema], name string) *Schema { + if v := cached.Load(); v != nil { + return v + } + + m, ok := s.members[name] + if !ok { + return nil + } + + cached.Store(m) + return m +} + +// MemberName returns the member component of the schema's shape ID. +func (s *Schema) MemberName() string { + return s.id.Member +} + +// ID returns the shape ID of the schema. +func (s *Schema) ID() ShapeID { + return s.id +} + +// TargetID returns the shape ID of the member's target shape. +func (s *Schema) TargetID() ShapeID { + return s.targetID +} + +// Type returns the shape type of the schema. +func (s *Schema) Type() ShapeType { + return s.typ +} + +// Member returns the member schema for the given name, or nil. +func (s *Schema) Member(name string) *Schema { + return s.members[name] +} + +// Members returns the schema's members as a map of name to schema. +func (s *Schema) Members() map[string]*Schema { + return s.members +} + +// OperationSchema describes an operation, which is essentially its own schema +// with additional pointers to its input and output. +type OperationSchema struct { + *Schema + Input, Output *Schema + + inputStream, outputStream bool +} + +// NewOperationSchema returns an OperationSchema for (input, output). +func NewOperationSchema(op, input, output *Schema) *OperationSchema { + return &OperationSchema{ + Schema: op, + Input: input, + Output: output, + inputStream: isEventStream(input), + outputStream: isEventStream(output), + } +} + +// IsInputEventStream reports whether this is an input event stream. +func (s *OperationSchema) IsInputEventStream() bool { + return s.inputStream +} + +// IsOutputEventStream reports whether this is an output event stream. +func (s *OperationSchema) IsOutputEventStream() bool { + return s.outputStream +} + +// ServiceSchema describes a service shape. +type ServiceSchema struct { + *Schema + Version string +} + +// NewServiceSchema returns a ServiceSchema for the given service shape. +func NewServiceSchema(schema *Schema, version string) *ServiceSchema { + return &ServiceSchema{Schema: schema, Version: version} +} + +// SchemaTrait returns the target trait on the schema if it exists. +// +// For member schemas this returns the effective trait, which is the trait +// declared directly on the member if present, else the trait inherited from +// the target shape. +func SchemaTrait[T Trait](s *Schema) (T, bool) { + return schemaTrait[T](s, false) +} + +// SchemaDirectTrait returns the target trait on the schema if it was +// declared directly on the schema. +// +// For member schemas this returns the trait only if it was declared on the +// member itself, ignoring any trait inherited from the target shape. For +// non-member schemas this is equivalent to [SchemaTrait]. +func SchemaDirectTrait[T Trait](s *Schema) (T, bool) { + return schemaTrait[T](s, true) +} + +func schemaTrait[T Trait](s *Schema, directOnly bool) (T, bool) { + var zero T + + if s == nil { + return zero, false + } + + if it, ok := Trait(zero).(IndexableTrait); ok { + idx := it.TraitIndex() + if idx >= len(s.indexed) { + return zero, false + } + if directOnly && s.directMask&(1< indexStreaming && m.indexed[indexStreaming] != nil { + return true + } + } + return false +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema_ext.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema_ext.go new file mode 100644 index 000000000..e98427ae3 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/schema_ext.go @@ -0,0 +1,38 @@ +package smithy + +import ( + "sync/atomic" + "unsafe" +) + +// ExtensionID identifies a schema extension slot. Each codec family +// (JSON, CBOR, etc.) uses a distinct slot to cache precomputed data. +type ExtensionID int + +const numExtensionSlots = 5 + +const ( + ExtJSON ExtensionID = iota // transport/http/protocol/internal/json + ExtCBOR // transport/http/protocol/internal/cbor + ExtXML // transport/http/protocol/internal/xml + ExtQuery // transport/http/protocol/internal/query + ExtHTTPBinding // transport/http/protocol/internal/httpbinding +) + +// SchemaExtension retrieves or lazily computes the extension for the given +// slot. build is called on first access for a schema and the result is cached. +// The build function must return a pointer to an immutable value. +func SchemaExtension[T any](s *Schema, id ExtensionID, build func(*Schema) *T) *T { + p := atomic.LoadPointer(&s.ext[id]) + if p != nil { + return (*T)(p) + } + return computeSchemaExtension(s, id, build) +} + +//go:noinline +func computeSchemaExtension[T any](s *Schema, id ExtensionID, build func(*Schema) *T) *T { + v := build(s) + atomic.StorePointer(&s.ext[id], unsafe.Pointer(v)) + return v +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/serde.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/serde.go new file mode 100644 index 000000000..a9effc565 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/serde.go @@ -0,0 +1,229 @@ +package smithy + +import ( + "fmt" + "io" + "math/big" + "time" + + "github.com/aws/smithy-go/document" +) + +// ShapeSerializer implements the marshaling of an in-code representation of a +// shape to an unspecified data format, which is determined by the +// implementation. +// +// A ShapeSerializer is consumed by the **code-generated** Serialize() method +// of a modeled structure. For example: +// +// func (v *PutItemInput) Serialize(s smithy.ShapeSerializer) { +// s.WriteStruct(schemas.PutItemInput) +// v.SerializeMembers(s) +// s.CloseStruct() +// } +// +// func (v *PutItemInput) SerializeMembers(s smithy.ShapeSerializer) { +// if v.TableName != nil { +// s.WriteString(schemas.PutItemInput_TableName, *v.TableName) +// } +// if v.Item != nil { +// serializeAttributeMap(s, schemas.PutItemInput_Item, v.Item) +// } +// // ... +// } +type ShapeSerializer interface { + Bytes() []byte + + WriteInt8(*Schema, int8) + WriteInt16(*Schema, int16) + WriteInt32(*Schema, int32) + WriteInt64(*Schema, int64) + WriteFloat32(*Schema, float32) + WriteFloat64(*Schema, float64) + WriteBool(*Schema, bool) + WriteString(*Schema, string) + WriteBigInt(*Schema, *big.Int) + WriteBigFloat(*Schema, *big.Float) + WriteBlob(*Schema, []byte) + WriteTime(*Schema, time.Time) + + WriteUnion(schema, variant *Schema) + CloseUnion() + WriteDocument(*Schema, document.Value) + WriteNil(*Schema) + + WriteStruct(*Schema) + CloseStruct() + + WriteList(*Schema) + CloseList() + + WriteMap(*Schema) + WriteKey(*Schema, string) + CloseMap() +} + +// ShapeDeserializer implements the unmarshaling from some unspecified data +// format to an in-code representation of a shape, which is determined by the +// implementation. +type ShapeDeserializer interface { + ReadInt8(*Schema, *int8) error + ReadInt16(*Schema, *int16) error + ReadInt32(*Schema, *int32) error + ReadInt64(*Schema, *int64) error + ReadFloat32(*Schema, *float32) error + ReadFloat64(*Schema, *float64) error + ReadBool(*Schema, *bool) error + ReadString(*Schema, *string) error + ReadBlob(*Schema, *[]byte) error + ReadTime(*Schema, *time.Time) error + ReadBigInt(*Schema, *big.Int) error + ReadBigFloat(*Schema, *big.Float) error + ReadNil(*Schema) (bool, error) + + ReadStruct(*Schema) error + ReadStructMember() (*Schema, error) + + ReadUnion(*Schema) (*Schema, error) + ReadDocument(*Schema, *document.Value) error + + ReadList(*Schema) error + ReadListItem(*Schema) (hasMoreElements bool, err error) + + ReadMap(*Schema) error + ReadMapKey(*Schema) (key string, hasMoreElements bool, err error) +} + +// Serializable is an entity that can describe itself to a ShapeSerializer to +// be encoded to some format. +// +// Unlike the standard library marshaler interfaces, which idiomatically encode +// to []byte, the output format and data type here is not specified at all. +// This is because Smithy shapes need to encode to a variety of formats or data +// carriers. For example, HTTP-binding JSON protocols need to serialize some +// members to bytes (the HTTP request body) and others directly to fields on +// the HTTP request itself (e.g. headers). +type Serializable interface { + Serialize(ShapeSerializer) +} + +// StreamingInput is implemented by input types that have a streaming blob +// payload (an io.Reader member with @httpPayload + @streaming). +type StreamingInput interface { + GetPayloadStream() io.Reader +} + +// StreamingOutput is implemented by output types that have a streaming blob +// payload (an io.ReadCloser member with @httpPayload + @streaming). +type StreamingOutput interface { + SetPayloadStream(io.ReadCloser) +} + +// Deserializable is an entity that can unmarshal itself from a +// ShapeDeserializer. +type Deserializable interface { + Deserialize(ShapeDeserializer) error +} + +// DeserializableError is implemented by modeled error types for a service. +type DeserializableError interface { + Deserializable + error +} + +// ReadUnion is a utility API for generated clients. +func ReadUnion(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { + ms, err := d.ReadUnion(schema) + if ms == nil || err != nil { + return err + } + + if err := memberFn(ms); err != nil { + return err + } + + for { + ms, err = d.ReadUnion(schema) + if err != nil { + return err + } + if ms == nil { + return nil + } + return fmt.Errorf("union has more than one non-nil member: %s", ms.MemberName()) + } +} + +// ReadStruct is a utility API for generated clients. +func ReadStruct(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { + if err := d.ReadStruct(schema); err != nil { + return err + } + + for { + ms, err := d.ReadStructMember() + if err != nil { + return err + } + + if ms == nil { + return nil + } + + if err := memberFn(ms); err != nil { + return err + } + } +} + +// ReadList is a utility API for generated clients. +func ReadList(d ShapeDeserializer, schema *Schema, memberFn func() error) error { + if err := d.ReadList(schema); err != nil { + return err + } + + var memberSchema *Schema + if schema != nil { + memberSchema = schema.ListMember() + } + + for { + ok, err := d.ReadListItem(memberSchema) + if !ok { + return nil + } + if err != nil { + return err + } + + if err := memberFn(); err != nil { + return err + } + } +} + +// ReadMap is a utility API for generated clients. +func ReadMap(d ShapeDeserializer, schema *Schema, memberFn func(string) error) error { + if err := d.ReadMap(schema); err != nil { + return err + } + + var keySchema *Schema + if schema != nil { + keySchema = schema.MapKey() + } + + for { + k, ok, err := d.ReadMapKey(keySchema) + if !ok { + return nil + } + if err != nil { + return err + } + + if err := memberFn(k); err != nil { + return err + } + } +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/sync/error.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/sync/error.go new file mode 100644 index 000000000..629207672 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/sync/error.go @@ -0,0 +1,53 @@ +package sync + +import "sync" + +// OnceErr wraps the behavior of recording an error +// once and signal on a channel when this has occurred. +// Signaling is done by closing of the channel. +// +// Type is safe for concurrent usage. +type OnceErr struct { + mu sync.RWMutex + err error + ch chan struct{} +} + +// NewOnceErr return a new OnceErr +func NewOnceErr() *OnceErr { + return &OnceErr{ + ch: make(chan struct{}, 1), + } +} + +// Err acquires a read-lock and returns an +// error if one has been set. +func (e *OnceErr) Err() error { + e.mu.RLock() + err := e.err + e.mu.RUnlock() + + return err +} + +// SetError acquires a write-lock and will set +// the underlying error value if one has not been set. +func (e *OnceErr) SetError(err error) { + if err == nil { + return + } + + e.mu.Lock() + if e.err == nil { + e.err = err + close(e.ch) + } + e.mu.Unlock() +} + +// ErrorSet returns a channel that will be used to signal +// that an error has been set. This channel will be closed +// when the error value has been set for OnceErr. +func (e *OnceErr) ErrorSet() <-chan struct{} { + return e.ch +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/trait.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/trait.go new file mode 100644 index 000000000..a45db96c0 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/trait.go @@ -0,0 +1,21 @@ +package smithy + +// Trait represents a trait applied to a shape in a Smithy model. Traits +// related to (de)serialization are included in code-generated Schemas for the +// client. +type Trait interface { + TraitID() ShapeID +} + +// IndexableTrait is optionally implemented by Trait values that have a +// reserved index in Schema's indexed trait slice. All traits defined in the +// traits package implement this interface. +// +// You SHOULD NOT implement this outside of a smithy-go trait unless you know +// what you are doing. If you implement this and return a value that collides +// with one of the primary serde-based indexed traits (see index.go) you will +// probably break something. +type IndexableTrait interface { + Trait + TraitIndex() int +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/http.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/http.go new file mode 100644 index 000000000..b06e9fed1 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/http.go @@ -0,0 +1,69 @@ +package traits + +import smithy "github.com/aws/smithy-go" + +// HTTPHeader represents smithy.api#httpHeader. +type HTTPHeader struct { + Name string +} + +// TraitID identifies the trait. +func (*HTTPHeader) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpHeader"} } + +// HTTPLabel represents smithy.api#httpLabel. +type HTTPLabel struct{} + +// TraitID identifies the trait. +func (*HTTPLabel) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpLabel"} } + +// HTTPPayload represents smithy.api#httpPayload. +type HTTPPayload struct{} + +// TraitID identifies the trait. +func (*HTTPPayload) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpPayload"} } + +// HTTPPrefixHeaders represents smithy.api#httpPrefixHeaders. +type HTTPPrefixHeaders struct { + Prefix string +} + +// TraitID identifies the trait. +func (*HTTPPrefixHeaders) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpPrefixHeaders"} } + +// HTTPQuery represents smithy.api#httpQuery. +type HTTPQuery struct { + Name string +} + +// TraitID identifies the trait. +func (*HTTPQuery) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpQuery"} } + +// HTTPQueryParams represents smithy.api#httpQueryParams. +type HTTPQueryParams struct{} + +// TraitID identifies the trait. +func (*HTTPQueryParams) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpQueryParams"} } + +// HTTPResponseCode represents smithy.api#httpResponseCode. +type HTTPResponseCode struct{} + +// TraitID identifies the trait. +func (*HTTPResponseCode) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpResponseCode"} } + +// HTTP represents smithy.api#http. +type HTTP struct { + Method string + URI string + Code int +} + +// TraitID identifies the trait. +func (*HTTP) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "http"} } + +// HTTPError represents smithy.api#httpError. +type HTTPError struct { + Code int +} + +// TraitID identifies the trait. +func (*HTTPError) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpError"} } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/index.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/index.go new file mode 100644 index 000000000..47733afc6 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/index.go @@ -0,0 +1,107 @@ +package traits + +// Trait index constants, ordered by frequency of occurrence across AWS API +// models. Lower indices are assigned to more common traits so that the +// per-schema indexed slice stays small. +const ( + indexJSONName = iota + indexHTTP + indexHTTPLabel + indexXMLName + indexHTTPQuery + indexEC2QueryName + indexHTTPError + indexHTTPHeader + indexSensitive + indexAWSQueryError + indexTimestampFormat + indexHTTPPayload + indexContextParam + indexHTTPResponseCode + indexHostLabel + indexXMLNamespace + indexXMLFlattened + indexStreaming + indexMediaType + indexHTTPQueryParams + indexEventPayload + indexHTTPPrefixHeaders + indexEventHeader + indexXMLAttribute + indexUnitShape +) + +// TraitIndex implements [smithy.IndexableTrait]. +func (*JSONName) TraitIndex() int { return indexJSONName } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTP) TraitIndex() int { return indexHTTP } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPLabel) TraitIndex() int { return indexHTTPLabel } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLName) TraitIndex() int { return indexXMLName } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPQuery) TraitIndex() int { return indexHTTPQuery } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*EC2QueryName) TraitIndex() int { return indexEC2QueryName } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPError) TraitIndex() int { return indexHTTPError } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPHeader) TraitIndex() int { return indexHTTPHeader } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*Sensitive) TraitIndex() int { return indexSensitive } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*AWSQueryError) TraitIndex() int { return indexAWSQueryError } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*TimestampFormat) TraitIndex() int { return indexTimestampFormat } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPPayload) TraitIndex() int { return indexHTTPPayload } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*ContextParam) TraitIndex() int { return indexContextParam } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPResponseCode) TraitIndex() int { return indexHTTPResponseCode } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HostLabel) TraitIndex() int { return indexHostLabel } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLNamespace) TraitIndex() int { return indexXMLNamespace } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLFlattened) TraitIndex() int { return indexXMLFlattened } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*Streaming) TraitIndex() int { return indexStreaming } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*MediaType) TraitIndex() int { return indexMediaType } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPQueryParams) TraitIndex() int { return indexHTTPQueryParams } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*EventPayload) TraitIndex() int { return indexEventPayload } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPPrefixHeaders) TraitIndex() int { return indexHTTPPrefixHeaders } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*EventHeader) TraitIndex() int { return indexEventHeader } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLAttribute) TraitIndex() int { return indexXMLAttribute } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*UnitShape) TraitIndex() int { return indexUnitShape } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/serde.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/serde.go new file mode 100644 index 000000000..25b7f0dd3 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/serde.go @@ -0,0 +1,56 @@ +package traits + +import smithy "github.com/aws/smithy-go" + +// JSONName represents smithy.api#jsonName. +type JSONName struct { + Name string +} + +// TraitID identifies the trait. +func (*JSONName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "jsonName"} } + +// MediaType represents smithy.api#mediaType. +type MediaType struct { + Type string +} + +// TraitID identifies the trait. +func (*MediaType) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "mediaType"} } + +// TimestampFormat represents smithy.api#timestampFormat. +type TimestampFormat struct { + Format string +} + +// TraitID identifies the trait. +func (*TimestampFormat) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "timestampFormat"} } + +// XMLAttribute represents smithy.api#xmlAttribute. +type XMLAttribute struct{} + +// TraitID identifies the trait. +func (*XMLAttribute) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlAttribute"} } + +// XMLFlattened represents smithy.api#xmlFlattened. +type XMLFlattened struct{} + +// TraitID identifies the trait. +func (*XMLFlattened) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlFlattened"} } + +// XMLName represents smithy.api#xmlName. +type XMLName struct { + Name string +} + +// TraitID identifies the trait. +func (*XMLName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlName"} } + +// XMLNamespace represents smithy.api#xmlNamespace. +type XMLNamespace struct { + URI string + Prefix string +} + +// TraitID identifies the trait. +func (*XMLNamespace) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlNamespace"} } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/traits.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/traits.go new file mode 100644 index 000000000..599be4e54 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/traits/traits.go @@ -0,0 +1,72 @@ +// Package traits defines representations of Smithy IDL traits that appear in +// code-generated schemas. +package traits + +import smithy "github.com/aws/smithy-go" + +// Sensitive represents smithy.api#sensitive. +type Sensitive struct{} + +// TraitID identifies the trait. +func (*Sensitive) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "sensitive"} } + +// EventHeader represents smithy.api#eventHeader. +type EventHeader struct{} + +// TraitID identifies the trait. +func (*EventHeader) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "eventHeader"} } + +// EventPayload represents smithy.api#eventPayload. +type EventPayload struct{} + +// TraitID identifies the trait. +func (*EventPayload) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "eventPayload"} } + +// Streaming represents smithy.api#streaming. +type Streaming struct{} + +// TraitID identifies the trait. +func (*Streaming) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "streaming"} } + +// HostLabel represents smithy.api#hostLabel. +type HostLabel struct{} + +// TraitID identifies the trait. +func (*HostLabel) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "hostLabel"} } + +// ContextParam represents smithy.rules#contextParam. +type ContextParam struct{} + +// TraitID identifies the trait. +func (*ContextParam) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.rules", Name: "contextParam"} } + +// AWSQueryError represents aws.protocols#awsQueryError. +type AWSQueryError struct { + ErrorCode string + StatusCode int +} + +// TraitID identifies the trait. +func (*AWSQueryError) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQueryError"} } + +// EC2QueryName represents aws.protocols#ec2QueryName. +type EC2QueryName struct { + Name string +} + +// TraitID identifies the trait. +func (*EC2QueryName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "ec2QueryName"} } + +// AWSQueryCompatible represents aws.protocols#awsQueryCompatible. +type AWSQueryCompatible struct{} + +// TraitID identifies the trait. +func (*AWSQueryCompatible) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQueryCompatible"} } + +// UnitShape is a synthetic trait applied to input/output shapes that were +// backfilled from Unit. It indicates the shape has no defined members and +// should be treated as absent for protocol serialization purposes. +type UnitShape struct{} + +// TraitID identifies the trait. +func (*UnitShape) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.go", Name: "unitShape"} } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/auth.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/auth.go index 58e1ab5ef..5b5adad0b 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/auth.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/auth.go @@ -5,6 +5,7 @@ import ( smithy "github.com/aws/smithy-go" "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/eventstream" ) // AuthScheme defines an HTTP authentication scheme. @@ -19,3 +20,11 @@ type AuthScheme interface { type Signer interface { SignRequest(context.Context, *Request, auth.Identity, smithy.Properties) error } + +// EventStreamSigner is an optional interface that a [Signer] can implement to +// support signing of event stream messages. If the resolved auth scheme's +// signer implements this interface, the event stream middleware will use it to +// wrap the outbound message stream with a signing layer. +type EventStreamSigner interface { + NewMessageSigner(ctx context.Context, r *Request, identity auth.Identity, props smithy.Properties) (eventstream.MessageSigner, error) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream.go new file mode 100644 index 000000000..251db8ac3 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream.go @@ -0,0 +1,209 @@ +package http + +import ( + "context" + "fmt" + "io" + "sync" + + "github.com/aws/smithy-go" + smithysync "github.com/aws/smithy-go/sync" +) + +// EventStreamWriter writes events to a stream using a ClientProtocol. +// +// The writer manages a background goroutine that facilitates the write loop. +// Calls to Send() on a writer will block until the message has been written. +// +// The writer doesn't know anything about signing. If event stream messages are +// getting signed by the client then the underlying io.Writer has already been +// wrapped to handle that at this point. +type EventStreamWriter struct { + protocol ClientProtocol + schema *smithy.Schema + + eventStream io.WriteCloser + stream chan singleflight + done chan struct{} + err *smithysync.OnceErr + + closeOnce sync.Once +} + +// we send one message at a time, the underlying write loop marshals these into +// the writer and reports back any error to the error channel +type singleflight struct { + variant *smithy.Schema + event smithy.Serializable + errCh chan<- error +} + +// NewEventStreamWriter returns an EventStreamWriter for the given schema. +func NewEventStreamWriter(protocol ClientProtocol, schema *smithy.Schema, stream io.WriteCloser) *EventStreamWriter { + w := &EventStreamWriter{ + protocol: protocol, + schema: schema, + + eventStream: stream, + stream: make(chan singleflight), + done: make(chan struct{}), + err: smithysync.NewOnceErr(), + } + + go w.writeStream() + + return w +} + +func (w *EventStreamWriter) writeStream() { + defer w.Close() + + for { + select { + case ev := <-w.stream: + err := w.protocol.SerializeEventMessage(w.schema, ev.variant, ev.event, w.eventStream) + if err != nil { + w.err.SetError(err) + } + ev.errCh <- err + case <-w.done: + return + } + } +} + +// Send writes a single event to the stream. +func (w *EventStreamWriter) Send(ctx context.Context, variant *smithy.Schema, event smithy.Serializable) error { + if err := w.err.Err(); err != nil { + return err + } + + errCh := make(chan error, 1) + select { + case w.stream <- singleflight{variant, event, errCh}: + case <-ctx.Done(): + return ctx.Err() + case <-w.done: + return fmt.Errorf("stream closed, unable to send event") + } + + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return ctx.Err() + case <-w.done: + return fmt.Errorf("stream closed, unable to send event") + } +} + +// Close signals end-of-stream and closes the underlying writer. Close is +// safe for concurrent calls. +func (w *EventStreamWriter) Close() error { + w.closeOnce.Do(func() { + close(w.done) + w.err.SetError(w.eventStream.Close()) + }) + return w.err.Err() +} + +// Err returns the first error encountered during writing. +func (w *EventStreamWriter) Err() error { + return w.err.Err() +} + +// ErrorSet returns a channel that is closed when an error occurs. +func (w *EventStreamWriter) ErrorSet() <-chan struct{} { + return w.err.ErrorSet() +} + +// EventStreamReader reads events from a stream using a ClientProtocol. +type EventStreamReader struct { + protocol ClientProtocol + schema *smithy.Schema + types *smithy.TypeRegistry + + eventStream io.ReadCloser + stream chan smithy.Deserializable + done chan struct{} + err *smithysync.OnceErr + + closeOnce sync.Once +} + +// NewEventStreamReader returns an EventStreamReader that deserializes events +// through the given protocol from r. The schema is the event stream union +// schema. +func NewEventStreamReader(protocol ClientProtocol, schema *smithy.Schema, types *smithy.TypeRegistry, stream io.ReadCloser) *EventStreamReader { + r := &EventStreamReader{ + protocol: protocol, + schema: schema, + types: types, + + eventStream: stream, + stream: make(chan smithy.Deserializable), + done: make(chan struct{}), + err: smithysync.NewOnceErr(), + } + + go r.readEventStream() + + return r +} + +func (r *EventStreamReader) readEventStream() { + defer r.Close() + defer close(r.stream) + + for { + event, err := r.protocol.DeserializeEventMessage(r.schema, r.types, r.eventStream) + if err != nil { + if err == io.EOF { + return + } + select { + case <-r.done: + return + default: + r.err.SetError(err) + return + } + } + + select { + case r.stream <- event: + case <-r.done: + return + } + } +} + +// Events returns the channel from which deserialized events can be read. +func (r *EventStreamReader) Events() <-chan smithy.Deserializable { + return r.stream +} + +// Close stops the reader and releases the underlying stream. Close is safe +// for concurrent calls. +func (r *EventStreamReader) Close() error { + r.closeOnce.Do(func() { + close(r.done) + r.eventStream.Close() + }) + return r.err.Err() +} + +// Err returns the first error encountered during reading. +func (r *EventStreamReader) Err() error { + return r.err.Err() +} + +// ErrorSet returns a channel that is closed when an error occurs. +func (r *EventStreamReader) ErrorSet() <-chan struct{} { + return r.err.ErrorSet() +} + +// Closed returns a channel that is closed when the reader is closed. +func (r *EventStreamReader) Closed() <-chan struct{} { + return r.done +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go new file mode 100644 index 000000000..f7d60dc76 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go @@ -0,0 +1,69 @@ +package http + +import ( + "context" + "fmt" + "io" + + "github.com/aws/smithy-go/middleware" +) + +type eventStreamWriterKey struct{} + +// GetInputStreamWriter returns the io.WriteCloser pipe used for the +// operation's input event stream. +func GetInputStreamWriter(ctx context.Context) io.WriteCloser { + writeCloser, _ := middleware.GetStackValue(ctx, eventStreamWriterKey{}).(io.WriteCloser) + return writeCloser +} + +func setInputStreamWriter(ctx context.Context, writeCloser io.WriteCloser) context.Context { + return middleware.WithStackValue(ctx, eventStreamWriterKey{}, writeCloser) +} + +// InitializeStreamWriter is a Finalize middleware that creates an in-memory +// pipe and sets it as the HTTP request body so event stream messages can be +// written after the request is sent. +type InitializeStreamWriter struct{} + +// AddInitializeStreamWriter adds the InitializeStreamWriter middleware to the +// provided stack. +func AddInitializeStreamWriter(stack *middleware.Stack) error { + return stack.Finalize.Add(&InitializeStreamWriter{}, middleware.After) +} + +// ID returns the identifier for the middleware. +func (i *InitializeStreamWriter) ID() string { + return "InitializeStreamWriter" +} + +// HandleFinalize is the middleware implementation. +func (i *InitializeStreamWriter) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type: %T", in.Request) + } + + inputReader, inputWriter := io.Pipe() + defer func() { + if err == nil { + return + } + _ = inputReader.Close() + _ = inputWriter.Close() + }() + + request, err = request.SetStream(inputReader) + if err != nil { + return out, metadata, err + } + in.Request = request + + ctx = setInputStreamWriter(ctx, inputWriter) + + return next.HandleFinalize(ctx, in) +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/host.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/host.go index db9801bea..b504a455d 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/host.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/host.go @@ -69,7 +69,7 @@ func ValidPortNumber(port string) bool { return true } -// ValidHostLabel returns whether the label is a valid RFC 3986 host label. +// ValidHostLabel returns whether the label is a valid RFC 952/1123 host label. func ValidHostLabel(label string) bool { if l := len(label); l == 0 || l > 63 { return false diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/middleware_close_response_body.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/middleware_close_response_body.go index 914338f2e..820e91e59 100644 --- a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/middleware_close_response_body.go +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/middleware_close_response_body.go @@ -8,9 +8,29 @@ import ( "github.com/aws/smithy-go/middleware" ) +// CloseResponseBody closes the HTTP response body. It leaves the body open only +// for a successful response whose payload is a caller-owned stream (isStreaming +// with a nil opErr); on error, or for a non-streaming response, it closes the +// body — an error response body is diagnostic, not a caller-owned stream. +func CloseResponseBody(ctx context.Context, resp *Response, isStreaming bool, opErr error) { + if resp == nil || resp.Body == nil { + return + } + if isStreaming && opErr == nil { + return + } + + if closeErr := resp.Body.Close(); closeErr != nil { + middleware.GetLogger(ctx).Logf(logging.Warn, "failed to close HTTP response body, this may affect connection reuse") + } +} + // AddErrorCloseResponseBodyMiddleware adds the middleware to automatically // close the response body of an operation request if the request response // failed. +// +// Deprecated: generated operation deserializers now close the response body +// via CloseResponseBody, so this middleware is no longer used. func AddErrorCloseResponseBodyMiddleware(stack *middleware.Stack) error { return stack.Deserialize.Insert(&errorCloseResponseBodyMiddleware{}, "OperationDeserializer", middleware.Before) } @@ -42,6 +62,9 @@ func (m *errorCloseResponseBodyMiddleware) HandleDeserialize( // AddCloseResponseBodyMiddleware adds the middleware to automatically close // the response body of an operation request, after the response had been // deserialized. +// +// Deprecated: generated operation deserializers now close the response body +// via CloseResponseBody, so this middleware is no longer used. func AddCloseResponseBodyMiddleware(stack *middleware.Stack) error { return stack.Deserialize.Insert(&closeResponseBody{}, "OperationDeserializer", middleware.Before) } diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/protocol.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/protocol.go new file mode 100644 index 000000000..80fc9e6f9 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/transport/http/protocol.go @@ -0,0 +1,27 @@ +package http + +import ( + "context" + "io" + + "github.com/aws/smithy-go" +) + +// ClientProtocol defines the interface through which client-side operation +// request/responses are (de)serialized across the wire. +// +// While a caller CAN define their own protocol, it is almost never necessary +// to do so. In practice, a generated client will utilize one of the predefined +// protocols implemented as part of the Smithy client runtime. +type ClientProtocol interface { + ID() smithy.ShapeID + SerializeRequest(context.Context, *smithy.OperationSchema, smithy.Serializable, *Request) error + DeserializeResponse(ctx context.Context, schema *smithy.OperationSchema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error + + // event stream APIs + HasInitialEventMessage() bool + SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error + DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) + SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error + DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error +} diff --git a/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/type_registry.go b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/type_registry.go new file mode 100644 index 000000000..3c4e02a18 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/vendor/github.com/aws/smithy-go/type_registry.go @@ -0,0 +1,70 @@ +package smithy + +import ( + "strings" +) + +// TypeRegistry creates an instance of a type based on its Smithy IDL shape ID. +// +// Generated clients have an exported package-level registry (named +// TypeRegistry) that holds all structure types for the service. +type TypeRegistry struct { + Entries map[string]*TypeRegistryEntry +} + +// RegistryEntry creates a type registry entry. +func RegistryEntry[T any](schema *Schema) *TypeRegistryEntry { + return &TypeRegistryEntry{ + Schema: schema, + New: func() any { + return new(T) + }, + } +} + +// DeserializableError provides an instance of a deserializable error structure +// for a given shape ID. +// +// The ID is given as a string here since this will be called in a context where +// a shape ID is a discriminator read in from some wire payload. +func (t *TypeRegistry) DeserializableError(id string) (DeserializableError, bool) { + return typeRegistryLookup[DeserializableError](t, id) +} + +// LookupEntry returns the registry entry for the given shape ID. +func (t *TypeRegistry) LookupEntry(id string) (*TypeRegistryEntry, bool) { + entry, ok := t.Entries[id] + if !ok { + entry, ok = t.lookupShortName(id) + } + return entry, ok +} + +// TypeRegistryEntry holds the schema and constructor for a registered shape. +type TypeRegistryEntry struct { + Schema *Schema + New func() any +} + +func (t *TypeRegistry) lookupShortName(id string) (*TypeRegistryEntry, bool) { + for key, e := range t.Entries { + if idx := strings.Index(key, "#"); idx != -1 && key[idx+1:] == id { + return e, true + } + } + return nil, false +} + +func typeRegistryLookup[T any](t *TypeRegistry, id string) (T, bool) { + entry, ok := t.Entries[id] + if !ok { + entry, ok = t.lookupShortName(id) + } + if !ok { + var v T + return v, false + } + + v, ok := entry.New().(T) + return v, ok +} diff --git a/openshift-tests/ccm-aws-tests/vendor/modules.txt b/openshift-tests/ccm-aws-tests/vendor/modules.txt index 59daa403b..2fcd08fdf 100644 --- a/openshift-tests/ccm-aws-tests/vendor/modules.txt +++ b/openshift-tests/ccm-aws-tests/vendor/modules.txt @@ -7,7 +7,7 @@ github.com/Masterminds/semver/v3 # github.com/antlr4-go/antlr/v4 v4.13.1 ## explicit; go 1.22 github.com/antlr4-go/antlr/v4 -# github.com/aws/aws-sdk-go-v2 v1.41.6 +# github.com/aws/aws-sdk-go-v2 v1.43.5 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/aws github.com/aws/aws-sdk-go-v2/aws/defaults @@ -50,10 +50,10 @@ github.com/aws/aws-sdk-go-v2/credentials/stscreds ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/feature/ec2/imds github.com/aws/aws-sdk-go-v2/feature/ec2/imds/internal/config -# github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 +# github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.36 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/internal/configsources -# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 +# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.36 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 # github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 @@ -64,6 +64,11 @@ github.com/aws/aws-sdk-go-v2/internal/ini github.com/aws/aws-sdk-go-v2/service/ec2 github.com/aws/aws-sdk-go-v2/service/ec2/internal/endpoints github.com/aws/aws-sdk-go-v2/service/ec2/types +# github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.36.5 +## explicit; go 1.24 +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/internal/endpoints +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types # github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.45.2 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 @@ -90,7 +95,7 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc/types github.com/aws/aws-sdk-go-v2/service/sts github.com/aws/aws-sdk-go-v2/service/sts/internal/endpoints github.com/aws/aws-sdk-go-v2/service/sts/types -# github.com/aws/smithy-go v1.25.0 +# github.com/aws/smithy-go v1.27.7 ## explicit; go 1.24 github.com/aws/smithy-go github.com/aws/smithy-go/auth @@ -102,7 +107,9 @@ github.com/aws/smithy-go/encoding/httpbinding github.com/aws/smithy-go/encoding/json github.com/aws/smithy-go/encoding/xml github.com/aws/smithy-go/endpoints +github.com/aws/smithy-go/endpoints/private/bdd github.com/aws/smithy-go/endpoints/private/rulesfn +github.com/aws/smithy-go/eventstream github.com/aws/smithy-go/internal/sync/singleflight github.com/aws/smithy-go/io github.com/aws/smithy-go/logging @@ -111,8 +118,10 @@ github.com/aws/smithy-go/middleware github.com/aws/smithy-go/private/requestcompression github.com/aws/smithy-go/ptr github.com/aws/smithy-go/rand +github.com/aws/smithy-go/sync github.com/aws/smithy-go/time github.com/aws/smithy-go/tracing +github.com/aws/smithy-go/traits github.com/aws/smithy-go/transport/http github.com/aws/smithy-go/transport/http/internal/io github.com/aws/smithy-go/waiter From e914c779809e1c0d23740e26961310dc5fd66e26 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 13 Aug 2026 16:46:15 -0300 Subject: [PATCH 20/22] e2e: add SDK-managed NLB tests with four-variant comparison matrix Add sdk_nlb.go for KAS-equivalent NLB provisioning and four SDK test variants (baseline, no preserve_client_ip, multi-client, multi no-cip) using DaemonSet healthservers for same-node rollout simulation. Document scenarios in health/TEST_CASES.md and update health/README.md. Co-authored-by: Cursor --- .../ccm-aws-tests/e2e/aws/health/README.md | 50 +- .../e2e/aws/health/TEST_CASES.md | 375 +++++ .../e2e/aws/lb_health_transition.go | 1205 ++++++++++++++++- .../ccm-aws-tests/e2e/aws/sdk_nlb.go | 606 +++++++++ 4 files changed, 2217 insertions(+), 19 deletions(-) create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md index bdfe0f7bc..858d9c073 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/README.md @@ -24,8 +24,10 @@ openshift-tests/ccm-aws-tests/ │ ├── main.go # /readyz control, X-Server-State headers, admin API │ └── Dockerfile # Multi-stage scratch build (~10MB) ├── e2e/aws/ -│ ├── lb_health_transition.go # Ginkgo test scenarios (5.5, 5.5-CAPA, 5.2) -│ └── health/ # Extractable package (zero parent-path imports) +│ ├── lb_health_transition.go # Ginkgo scenarios (5.5, 5.5-CAPA, 5.2, 5.5-CLB, 5.5-SDK×4) +│ ├── sdk_nlb.go # SDK-managed NLB create/delete, preserve_client_ip helper +│ └── health/ +│ ├── TEST_CASES.md # Human-readable scenario docs + diagrams │ ├── types.go # HealthEvent, RequestRecord, TargetSnapshot │ ├── observer.go # TG health polling, PollOnce, TG attribute R/W │ ├── client.go # HTTP client with httptrace (new TCP per request) @@ -162,6 +164,33 @@ Flow: 4. Report timing table ``` +### Scenario 5.5-CLB — Pre-Readyz CLB Baseline (OCPBUGS-86789) + +Same flow as 5.5 but uses a Classic Load Balancer (no NLB annotation). +Control group for NLB-specific behaviour. + +### SDK-Managed NLB Variants (KAS-equivalent) + +Four comparable tests using AWS SDK to provision the NLB (same stack as the +real KAS NLB). Healthserver runs as a **DaemonSet** on control-plane nodes +(same-node replacement on rollout). See **`TEST_CASES.md`** for diagrams and +run filters. + +| Scenario | Client | preserve_client_ip | Purpose | +|----------|--------|-------------------|---------| +| 5.5-SDK | 1 pod, 32 workers | true | Baseline (single client IP → skewed traffic) | +| 5.5-SDK-no-cip | 1 pod, 32 workers | false | Isolate stickiness with single client | +| 5.5-SDK-multi | DaemonSet / worker | true | **Recommended** — even traffic, KAS-faithful | +| 5.5-SDK-multi-no-cip | DaemonSet / worker | false | Multi-client control (no stickiness) | + +**Plan:** `ai-plans/lb-health-transition-e2e-plan-v21-multi-client.md` +**Depends on:** v19 (SDK NLB), v20 (healthserver DaemonSet) + +```sh +# Example: recommended variant +$BIN run-test "...multi-client (OCPBUGS-86789) should not route..." +``` + ## Components ### Healthserver (`cmd/healthserver/`) @@ -287,12 +316,15 @@ done < <($BIN list tests 2>/dev/null \ | jq -r '.[].name' \ | grep "health-transition") -# Run a specific scenario -$BIN run-test "...(OCPBUGS-86789) should not route to pre-readyz targets..." -$BIN run-test "...(SPLAT-307) should stop routing within shutdown-delay..." -$BIN run-test "...(OCPBUGS-86789) should not route to pre-readyz targets with connection-termination..." +# Run SDK variant (see TEST_CASES.md for all filters) +$BIN run-test "...SDK-managed NLB pre-readyz routing (KAS-equivalent)..." +$BIN run-test "...preserve_client_ip=false..." +$BIN run-test "...multi-client (OCPBUGS-86789) should not route..." +$BIN run-test "...multi-client preserve_client_ip=false..." ``` +**Full scenario documentation:** `e2e/aws/health/TEST_CASES.md` + ## Report Output Each test produces a single-block report (one `framework.Logf` call to @@ -334,10 +366,12 @@ avoid per-line logger timestamps) containing: - Multiple iterations per scenario (configurable repeat count) - Configurable delays via environment variables - Per-second CSV output matching SPLAT-307 format -- CLB comparison variant (control group) -- JSON machine-readable report for cross-run comparison +- JSON machine-readable report for cross-run comparison (SDK 4-variant matrix) - EC2 instance ID → node name mapping in observer events - Scenario 5.6: node replacement (deregistration path) - Scenario 5.7: connection termination regression guard (OCPBUGS-55626) - Periodic CI job in CCCMO + +**Done:** CLB baseline (5.5-CLB), SDK-managed NLB (v19), DaemonSet rollout (v20), +SDK 4-variant matrix (v21) - Multi-region runs (us-west-2, eu-west-1) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md new file mode 100644 index 000000000..b4665399f --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md @@ -0,0 +1,375 @@ +# LB Health Transition Test Cases + +Tests validating AWS Load Balancer health-check behaviour during a KAS +(Kubernetes API Server) graceful rollout. The core question: **does the NLB +route traffic to a pod before `/readyz` returns 200?** + +Each scenario uses a `healthserver` binary that mimics KAS lifecycle signals +on port `19443`. An in-cluster HTTP client fires ~320 req/s through the load +balancer and an aggregator collects every request record. + +--- + +## Shared Concepts + +### KAS Graceful Shutdown Model + +``` +Pod lifecycle /readyz state NLB target state +───────────────────────────────────────────────────────────────── + 200 OK HEALTHY ← traffic flows +SIGTERM received + └─ sets readyz → 503 503 HEALTHY ← traffic still flows + └─ keeps serving ~135s (NLB HC not propagated yet) + 503 UNHEALTHY ← NLB stops routing + └─ process exits + (port closed, TCP RST) +New pod starts + └─ startup delay (boot) — UNHEALTHY (port not up yet) + └─ port bound — UNHEALTHY (HC not passed yet) + └─ /readyz → 200 200 OK UNHEALTHY (HC polling: ~20s) + 200 OK HEALTHY ← traffic flows again +``` + +### Timing Milestones (all scenarios) + +``` +t0 Deployment/DaemonSet created +t1 All pods Running (healthserver up, startup delay pending) +t2 NLB / LB provisioned (DNS assigned) +t3 All TG targets HEALTHY (first HC cycle passed) +t4 First client request successfully routed + +t5 readyz → 503 (SIGTERM sent / admin signal) +t6 TG target transitions to UNHEALTHY (~20 s after t5) +t7 Last request routed to target after t5 (NLB drains connection) + +t7.1 Pod delete sent (SIGTERM delivered by kubelet) +t7.3 New pod TCP port bound (from X-Server-Start-Time) +t7.4 First pre-readyz request (BUG if present) + +t8 readyz → 200 (new pod ready) +t9 TG target transitions to HEALTHY (~20 s after t8) +t10 First request routed to new pod +``` + +--- + +## Scenario 5.5 — NLB Pre-Readyz Routing (OCPBUGS-86789) + +**Bug being tested:** Does the AWS NLB route traffic to a restarted instance +before its `/readyz` health check passes? If yes → OCPBUGS-86789 is +reproduced. + +**Workload:** Kubernetes-managed NLB (`type: LoadBalancer` with +`aws-load-balancer-type: nlb`). Pods scheduled on control-plane nodes via +Deployment. + +``` + CLIENT (in-cluster, worker node) + │ ~320 req/s via NLB DNS + ▼ + ┌────────────────┐ + │ NLB │ HC: HTTP /readyz, interval=10s, threshold=2 + │ (k8s-managed) │ Target type: instance + └───┬────┬───┬───┘ + │ │ │ + ┌────┘ ┌──┘ └──┐ + ▼ ▼ ▼ + [node-A] [node-B] [node-C] ← control-plane nodes (hostNetwork) + pod-TARGET pod-2 pod-3 ← healthserver on port 19443 + + +Phase 1 — STEADY STATE (t3 → t5) +─────────────────────────────────── + All 3 targets HEALTHY, traffic distributed across all 3 pods. + Expect: 0 pre-readyz requests. + +Phase 2 — GRACEFUL SHUTDOWN (t5 → t7) +──────────────────────────────────────── + t5: SIGTERM → pod-TARGET sets /readyz → 503, keeps serving + t6: ~20s later, NLB HC detects UNHEALTHY + t7: NLB stops routing to node-A + + Timeline on node-A: + ┌────────────────────────────────────────────────────────┐ + │ t5 t6 (~+20s) t7 (~+31s) │ + │ ├───────────┤─────────────┤ │ + │ readyz=503 HC=UNHEALTHY last routed req │ + │ ↑ still receives traffic ↑ │ + └────────────────────────────────────────────────────────┘ + Expected: traffic continues for ~20-31s (HC propagation delay) — NOT a bug. + +Phase 3 — RESTART (t7 → t9) +────────────────────────────── + t7.1: kubelet deletes pod-TARGET on node-A + ····· node-A: port 19443 CLOSED (after terminationGracePeriodSeconds) + ····· DaemonSet/Deployment creates replacement pod on node-A + t7.3: new pod binds port 19443 (TCP up, /readyz still returning draining/503) + t8: new pod /readyz → 200 + + ┌─────────────────────────────────────────────────────────────┐ + │ t7.1 port closed t7.3 port up t8 readyz=200 │ + │ ├──────────────────────┤────────────────┤ │ + │ ↑ ↑ │ + │ pre-readyz HC polling (~20s) │ + │ window │ + │ (BUG ZONE: should NLB route here?) │ + └─────────────────────────────────────────────────────────────┘ + + PASS: NLB does NOT route to node-A during pre-readyz window + BUG: NLB DOES route to node-A before t8 → OCPBUGS-86789 + +Phase 4 — RECOVERY (t9 → end) +──────────────────────────────── + t9: NLB HC detects HEALTHY on node-A + t10: First client request routed to new pod + Expect: traffic resumes on all 3 nodes, 0 errors. + + +VERDICT logic +───────────── + [OK] PreReadyzReqCount == 0 AND no unhealthy reqs during Restart + [BUG] X-Server-State: pre-readyz received → reproduces OCPBUGS-86789 + [SHUTDOWN] Requests after readyz→503 (expected, NLB propagation delay) + [RESTART] Unhealthy/pre-readyz reqs during Restart phase (NLB re-routed too early) +``` + +--- + +## Scenario 5.5-SDK — SDK-Managed NLB Baseline (OCPBUGS-86789) + +**Report label:** `5.5-SDK (Pre-Readyz Routing KAS-Equivalent / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing (KAS-equivalent) (OCPBUGS-86789)` + +**Why:** The KAS NLB is provisioned directly via AWS SDK (not via `type: LoadBalancer`). +This scenario creates an identical NLB manually to test the same pre-readyz routing +question on the exact same stack KAS uses. + +| Parameter | Value | +|-----------|-------| +| NLB | AWS SDK, internal, `instance:port` targets | +| Healthserver | DaemonSet on control-plane, hostNetwork, port 19443 | +| Client | 1 pod on worker, **32 workers** × 50ms (~640 req/s) | +| preserve_client_ip | **true** (default, matches real KAS NLB) | + +``` +[client pod] (1 IP, 32 workers) + │ + ▼ + SDK NLB (preserve_client_ip=true) → hash by client IP → mostly 1 target + │ + ▼ + [node-A] [node-B] [node-C] ← DaemonSet healthserver, 1 pod/node +``` + +**Known limitation:** With a single client IP, NLB stickiness sends ~96% of traffic +to one target. Post-rollout timing metrics (T_pod_restart, T_route_start) may be N/A +for the restarted pod until multi-client variants are used. + +**Run:** +```bash +$BIN run-test "[cloud-provider-aws-e2e-openshift] loadbalancer health-transition SDK-managed NLB pre-readyz routing (KAS-equivalent) (OCPBUGS-86789)" +``` + +**Code:** `lb_health_transition.go` ~line 630 + +--- + +## Scenario 5.5-SDK-no-cip — Single Client, No Source-IP Stickiness + +**Report label:** `5.5-SDK-no-cip (preserve_client_ip=false / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing, preserve_client_ip=false (OCPBUGS-86789)` + +Identical to **5.5-SDK** except `preserve_client_ip.enabled=false` is set on the TG +after creation via `setTGPreserveClientIP()`. + +| Parameter | Value | +|-----------|-------| +| Diff vs 5.5-SDK | TG attribute `preserve_client_ip.enabled=false` only | +| Client | 1 pod, 32 workers | +| Expected distribution | ~even across 3 targets (no IP hash stickiness) | + +**Purpose:** Isolate whether source-IP stickiness affects pre-readyz routing when +using a single client pod. + +**Run:** +```bash +$BIN run-test "...preserve_client_ip=false..." +``` + +**Code:** `lb_health_transition.go` ~line 893 + +--- + +## Scenario 5.5-SDK-multi — Multi-Client, Source-IP Stickiness + +**Report label:** `5.5-SDK-multi (Multi-Client DaemonSet / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing, multi-client (OCPBUGS-86789)` + +Identical to **5.5-SDK** except the client is a **DaemonSet** (one pod per worker node). +Each pod has a distinct source IP → NLB distributes traffic across all targets even +with `preserve_client_ip=true` (same as real KAS clients from many node IPs). + +``` +[client-ds on worker-1] (IP-1) ──┐ +[client-ds on worker-2] (IP-2) ──┼──→ SDK NLB (preserve_client_ip=true) +[client-ds on worker-3] (IP-3) ──┘ ↓ + [node-A] [node-B] [node-C] +``` + +| Parameter | Value | +|-----------|-------| +| Client | DaemonSet on workers, **16 workers** × 50ms per pod | +| preserve_client_ip | **true** | +| Records | `fetchMergedClientRecords()` from all client pods | +| Expected distribution | ~33% per target | + +**Purpose:** Fair KAS-equivalent test — even traffic + observable post-restart metrics +on the rolled target. + +**Run:** +```bash +$BIN run-test "...multi-client (OCPBUGS-86789) should not route..." +# Avoid matching the multi-no-cip It name +``` + +**Code:** `lb_health_transition.go` ~line 1115 + +--- + +## Scenario 5.5-SDK-multi-no-cip — Multi-Client, No Source-IP Stickiness + +**Report label:** `5.5-SDK-multi-no-cip (Multi-Client + preserve_client_ip=false / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing, multi-client preserve_client_ip=false (OCPBUGS-86789)` + +Combines **5.5-SDK-multi** (client DaemonSet) with **5.5-SDK-no-cip** +(`preserve_client_ip=false`). + +| Parameter | Value | +|-----------|-------| +| Client | DaemonSet on workers, 16 workers × 50ms per pod | +| preserve_client_ip | **false** | +| Expected distribution | ~even (multi-client + no stickiness) | + +**Purpose:** Control for both variables — tests pre-readyz behaviour with maximum +traffic spread and no source-IP affinity. + +**Run:** +```bash +$BIN run-test "...multi-client preserve_client_ip=false..." +``` + +**Code:** `lb_health_transition.go` ~line 1335 + +--- + +## SDK Variants — Comparison Matrix + +All four share: healthserver DaemonSet on masters, SDK-managed NLB, same HC/TG config +(HTTP `/readyz`, interval=10s, threshold=2), same rollout simulation (delete one pod, +wait for same-node replacement). **Only client topology and `preserve_client_ip` differ.** + +| Scenario | Client | preserve_client_ip | Traffic spread | KAS-faithful | +|----------|--------|-------------------|----------------|--------------| +| 5.5-SDK | 1 pod, 32w | true | Skewed (~1 target) | NLB yes, clients no | +| 5.5-SDK-no-cip | 1 pod, 32w | false | Even | NLB no | +| 5.5-SDK-multi | DS/worker, 16w | true | Even | **Yes (recommended)** | +| 5.5-SDK-multi-no-cip | DS/worker, 16w | false | Even | Clients no | + +**Shared AWS lifecycle** (all SDK variants): see v19 plan (`sdk_nlb.go`). +Cleanup includes SG retry on `DependencyViolation` and idempotent SG create on reruns. + +**Plan:** `ai-plans/lb-health-transition-e2e-plan-v21-multi-client.md` + +--- + +## Scenario 5.5-CAPA — NLB with CAPA TG Attributes (OCPBUGS-86789) + +Same as 5.5 but applies the CAPA-specific Target Group attributes after TG +creation (`connection_termination.enabled=false`, `draining_interval=300s`). +Tests whether CAPA's fix attributes change the pre-readyz routing behaviour. + +``` + TG attributes applied post-creation: + connection_termination.enabled = false + target_health_state.unhealthy.draining_interval_seconds = 300 + + Verdict adds: + [DRAINING] requests with X-Server-State: draining (300s window) +``` + +--- + +## Scenario 5.2 — NLB Shutdown Propagation (SPLAT-307) + +**Question:** How long does it take the NLB to stop routing to a target after +it signals `/readyz → 503`? (No pod restart — measures propagation delay only.) + +``` + t5 readyz → 503 (admin signal, no pod delete) + t6 NLB HC detects UNHEALTHY + t7 Last routed request to target + + ┌────────────────────────────────┐ + │ t5 t6 (~+20s) t7 │ + │ ├─────┤────────────┤ │ + │ T_tg_unhealthy │ + │ T_route_stop │ + └────────────────────────────────┘ + + Expected: T_route_stop ≈ T_tg_unhealthy (no extra routing after HC flips) + Bug: T_route_stop >> T_tg_unhealthy (extra requests after HC detects it) +``` + +--- + +## Scenario 5.5-CLB — Classic Load Balancer Baseline (OCPBUGS-86789) + +Same test as 5.5 but using a Classic Load Balancer (`type: LoadBalancer` with +no NLB annotation). Provides a CLB vs NLB comparison to determine if +pre-readyz routing is NLB-specific or general to all AWS LBs. + +``` + CLB differences: + - TCP proxy (no HTTP routing) + - Connection-level health checks (not HTTP /readyz) + - No target group abstraction + - Different HC propagation timing + + Expected: CLB may show different pre-readyz window than NLB +``` + +--- + +## Summary Table + +| Scenario | LB Type | Managed by | Workload | Client | preserve_client_ip | Tests | +|----------|---------|------------|----------|--------|-------------------|-------| +| 5.5 | NLB | Kubernetes | Deployment | 1 pod | true (svc default) | Pre-readyz (OCPBUGS) | +| 5.5-CAPA | NLB | Kubernetes | Deployment | 1 pod | true | Pre-readyz + CAPA TG | +| 5.5-SDK | NLB | AWS SDK | DaemonSet | 1 pod, 32w | true | KAS-equivalent baseline | +| 5.5-SDK-no-cip | NLB | AWS SDK | DaemonSet | 1 pod, 32w | **false** | Stickiness isolation | +| 5.5-SDK-multi | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | **Recommended KAS-faithful** | +| 5.5-SDK-multi-no-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | Multi + no stickiness | +| 5.2 | NLB | Kubernetes | Deployment | 1 pod | true | Shutdown propagation (SPLAT-307) | +| 5.5-CLB | CLB | Kubernetes | Deployment | 1 pod | N/A | Pre-readyz CLB baseline | + +**Pass criteria (all 5.5* scenarios):** +- `PreReadyzReqCount == 0` — no requests before `/readyz → 200` +- Unhealthy requests during Restart phase == 0 (informational in verdict, not hard fail) + +**Informational (always reported, not a failure):** +- Shutdown propagation delay (t5→t7, ~20–35s) — expected NLB HC lag + +## Related Plans + +| Plan | Topic | +|------|-------| +| `ai-plans/lb-health-transition-e2e-plan-v19-sdk-managed-nlb.html` | SDK NLB creation, infra discovery | +| `ai-plans/lb-health-transition-e2e-plan-v20-daemonset-rollout.md` | Healthserver DaemonSet, same-node rollout | +| `ai-plans/lb-health-transition-e2e-plan-v21-multi-client.md` | Four SDK variants, client scaling, preserve_client_ip matrix | diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 32ea71999..228da5a6a 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -619,6 +619,936 @@ var _ = Describe(healthTransitionTestPrefix, func() { framework.Logf("\n%s", report) }) }) + + // ── Scenario 5.5 SDK-managed NLB (KAS-equivalent) ─────────────────── + // Creates the NLB directly via AWS SDK with instance:19443 targets, + // replicating how the OCP installer creates the KAS NLB with + // instance:6443 targets. Both traffic AND health checks go to the + // same port (19443) on the same path — no kube-proxy, no NodePort, + // no K8s Service. This is the closest reproduction of the actual + // KAS NLB setup where OCPBUGS-86789 is observed. + Context("SDK-managed NLB pre-readyz routing (KAS-equivalent) (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting matching KAS NLB", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + + deployName := "healthserver" + + // Register cleanup FIRST, before creating any resources. + // The cleanup function captures variables by reference — they're + // populated as resources are created during setup. + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-managed NLB test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-client", metav1.DeleteOptions{}) + }) + + // ── Deploy aggregator ── + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + framework.Logf("[aggregator] ready at %s", aggregatorURL) + + // ── SCC for hostNetwork ── + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + // ── Deploy healthserver pods via DaemonSet (one per master node) ── + // DaemonSet guarantees same-node replacement on pod deletion, + // matching KAS static pod rollout behavior. + By("creating healthserver DaemonSet (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSet(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + framework.Logf("[daemonset] %d pods ready on master nodes", replicas) + + // ── Discover cluster infrastructure ── + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + framework.Logf("[infra] infraID=%s vpc=%s subnets=%v instances=%v sg=%s", + infra.InfraID, infra.VPCID, infra.SubnetIDs, infra.InstanceIDs, infra.MasterSGID) + + // ── Add SG rule for port 19443 ── + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + // ── Create NLB via SDK ── + By("creating SDK-managed NLB with instance:19443 targets") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort)) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + framework.Logf("[sdk-nlb] NLB DNS: %s", sdkNLB.NLBDNS) + + // DeferCleanup is already registered at the top of this test. + // Populate the sdkNLB variable so cleanup knows what to delete. + + // ── Create TG observer (reuses existing NLB observer since SDK NLB uses ELBv2) ── + observer := health.NewObserver(elbClient, 1*time.Second) + // The TG ARN is already known from createSDKManagedNLB + // Set it directly on the observer by discovering from the NLB ARN + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + framework.Logf("[sdk-nlb] TG ARN: %s (target type: %s)", observer.TargetGroupARN(), observer.TargetType()) + + // ── Wait for all targets healthy ── + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + // ── Deploy in-cluster client (pointing to SDK NLB DNS) ── + // Use double the default workers for better resolution of the + // narrow pre-readyz window (~10s HC interval). + By("deploying in-cluster client on worker node") + clientPodName := deployInClusterClient(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers*2, defaultClientInterval) + + // ── Build service config for report ── + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + + // Fetch TG attributes for report + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + // ── Start observer + TG push ── + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + framework.Logf("[observer] started TG health polling (1s) + aggregator push (2s)") + framework.Logf("[client-pod] in-cluster client %s sending to SDK NLB", clientPodName) + defer func() { stopTGPush(); observer.Stop() }() + + // ── Steady state ── + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + framework.Logf("[steady] %d requests from in-cluster client, %d non-ready", len(steadyRecords), steadyNonReady) + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + // ── Pick target ── + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + // ── Delete pod (SIGTERM triggers readyz→503) ── + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + // waitForNewPod only skips the old pod by name, so it would + // immediately return one of the other still-running DaemonSet pods. + // Use waitForNewPodFromSet which requires a pod name NOT in knownServers. + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s — port conflict with terminating pod?", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + // ── Wait for TG unhealthy then healthy ── + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + // ── Collect + report ── + allRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK (Pre-Readyz Routing KAS-Equivalent / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + + report += buildVerdict55(tl, allRecords) + + framework.Logf("\n%s", report) + }) + }) + + // ── Scenario 5.5-SDK-no-cip ───────────────────────────────────────── + // Identical to 5.5-SDK but with preserve_client_ip.enabled=false on the + // TG. The NLB then distributes connections across targets without source-IP + // stickiness, so all 3 targets receive traffic even from a single client pod. + // Allows direct comparison with 5.5-SDK to isolate the preserve_client_ip effect. + Context("SDK-managed NLB pre-readyz routing, preserve_client_ip=false (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting and preserve_client_ip disabled", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + deployName := "healthserver" + + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-no-cip test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-client", metav1.DeleteOptions{}) + }) + + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + By("creating healthserver DaemonSet (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSet(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + By("creating SDK-managed NLB with instance:19443 targets") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort)) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + + By("disabling preserve_client_ip on TG (so NLB distributes across all targets)") + err = setTGPreserveClientIP(ctx, elbClient, sdkNLB.TGARN, false) + framework.ExpectNoError(err, "set preserve_client_ip=false") + + observer := health.NewObserver(elbClient, 1*time.Second) + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + By("deploying in-cluster client on worker node") + clientPodName := deployInClusterClient(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers*2, defaultClientInterval) + + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "preserve_client_ip": "false", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + defer func() { stopTGPush(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchClientRecords(ctx, cs, ns.Name, clientPodName) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK-no-cip (preserve_client_ip=false / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + report += buildVerdict55(tl, allRecords) + framework.Logf("\n%s", report) + }) + }) + + // ── Scenario 5.5-SDK-multi ─────────────────────────────────────────── + // Identical to 5.5-SDK but deploys one client pod per worker node + // (DaemonSet). Each pod has a distinct source IP so the NLB distributes + // traffic across all targets with preserve_client_ip=true (same as real + // KAS clients coming from different node IPs). Records from all client + // pods are merged before analysis. + Context("SDK-managed NLB pre-readyz routing, multi-client (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting and multiple client IPs", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + deployName := "healthserver" + clientDSName := "healthtest-client" + + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-multi test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, clientDSName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + }) + + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + By("creating healthserver DaemonSet (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSet(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + By("creating SDK-managed NLB with instance:19443 targets") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort)) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + + observer := health.NewObserver(elbClient, 1*time.Second) + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + // One client pod per worker node — each has a unique source IP, + // so the NLB distributes traffic across all 3 targets. + By("deploying client DaemonSet on worker nodes (one pod per worker)") + clientPodNames := deployClientDaemonSet(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval) + + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "client-mode": "multi-client-daemonset", + "preserve_client_ip": "true", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + defer func() { stopTGPush(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK-multi (Multi-Client DaemonSet / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + report += buildVerdict55(tl, allRecords) + framework.Logf("\n%s", report) + }) + }) + + // ── Scenario 5.5-SDK-multi-no-cip ─────────────────────────────────── + // Identical to 5.5-SDK-multi but with preserve_client_ip.enabled=false. + // Isolates whether multi-client distribution changes pre-readyz behaviour + // when source-IP stickiness is disabled. + Context("SDK-managed NLB pre-readyz routing, multi-client preserve_client_ip=false (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting, multiple client IPs, and preserve_client_ip disabled", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + deployName := "healthserver" + clientDSName := "healthtest-client" + + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-multi-no-cip test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, clientDSName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + }) + + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + By("creating healthserver DaemonSet (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSet(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + By("creating SDK-managed NLB with instance:19443 targets") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort)) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + + By("disabling preserve_client_ip on TG (so NLB distributes across all targets)") + err = setTGPreserveClientIP(ctx, elbClient, sdkNLB.TGARN, false) + framework.ExpectNoError(err, "set preserve_client_ip=false") + + observer := health.NewObserver(elbClient, 1*time.Second) + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + By("deploying client DaemonSet on worker nodes (one pod per worker)") + clientPodNames := deployClientDaemonSet(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval) + + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "client-mode": "multi-client-daemonset", + "preserve_client_ip": "false", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + defer func() { stopTGPush(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK-multi-no-cip (Multi-Client + preserve_client_ip=false / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + report += buildVerdict55(tl, allRecords) + framework.Logf("\n%s", report) + }) + }) }) // ─── Setup helper ─────────────────────────────────────────────────────────── @@ -755,7 +1685,8 @@ func setupHealthTransition( // to the NLB with ~1ms RTT (vs ~430ms from external), achieving much // higher throughput for better detection coverage. By("deploying in-cluster client on worker node") - clientPodName = deployInClusterClient(ctx, cs, ns.Name, image, lbDNS, aggregatorURL) + clientPodName = deployInClusterClient(ctx, cs, ns.Name, image, lbDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval) return lbDNS, observer, cfg, setupTimes, clientPodName } @@ -914,6 +1845,35 @@ func waitForNewPod(ctx context.Context, cs clientset.Interface, namespace, deplo return newPod } +// waitForNewPodFromSet waits for a Running pod whose name is NOT in knownPods. +// Use this instead of waitForNewPod when the workload is a DaemonSet: unlike a +// Deployment, the other DaemonSet pods are already Running and share the same +// label, so waitForNewPod would immediately return one of them. +func waitForNewPodFromSet(ctx context.Context, cs clientset.Interface, namespace, deployName string, knownPods map[string]bool) string { + var newPod string + err := wait.PollUntilContextTimeout(ctx, 2*time.Second, 10*time.Minute, true, func(ctx context.Context) (bool, error) { + pods, err := cs.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + if err != nil { + return false, nil + } + for i := range pods.Items { + p := &pods.Items[i] + if knownPods[p.Name] || p.DeletionTimestamp != nil { + continue + } + if p.Status.Phase == v1.PodRunning { + newPod = p.Name + return true, nil + } + } + return false, nil + }) + framework.ExpectNoError(err, "wait for replacement pod (from known set)") + return newPod +} + // ─── Timeline computation ─────────────────────────────────────────────────── // isUnhealthyState returns true for any unhealthy TG state, including @@ -1243,6 +2203,32 @@ func buildReport( w(" Duration: %s", testDuration.Truncate(time.Second)) w(" Avg rate: %.1f req/s", avgReqsPerSec) + // Print unique error messages (deduplicated) to help diagnose routing issues. + if reqsErr > 0 { + errCounts := make(map[string]int) + for _, r := range records { + if r.Error != "" { + errCounts[r.Error]++ + } + } + w("") + w(" ERROR SAMPLES (%d unique):", len(errCounts)) + shown := 0 + for msg, count := range errCounts { + if shown >= 5 { + w(" ... and %d more unique errors", len(errCounts)-shown) + break + } + // Truncate very long error messages. + display := msg + if len(display) > 200 { + display = display[:200] + "..." + } + w(" [%dx] %s", count, display) + shown++ + } + } + // ── Per-phase request breakdown ── // Phases are defined by the timeline milestones: // Warmup: t3→t5 (all targets healthy, steady-state traffic) @@ -1662,6 +2648,98 @@ func buildHealthserverDeployment(namespace, name string, replicas int32, startup } } +// buildHealthserverDaemonSet creates a DaemonSet spec with the same pod +// template as buildHealthserverDeployment. A DaemonSet guarantees one pod per +// matching node and same-node replacement on pod deletion, which matches KAS +// static pod rollout behavior for NLB health transition testing. +func buildHealthserverDaemonSet(namespace, name string, startupDelay time.Duration, image string, aggregatorURL ...string) *appsv1.DaemonSet { + labels := map[string]string{"app": name} + return &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{Labels: labels}, + Spec: v1.PodSpec{ + HostNetwork: true, + DNSPolicy: v1.DNSClusterFirstWithHostNet, + NodeSelector: map[string]string{ + "node-role.kubernetes.io/control-plane": "", + }, + TerminationGracePeriodSeconds: ptrInt64(int64(kasShutdownDelay.Seconds())), + Tolerations: []v1.Toleration{ + {Key: "node-role.kubernetes.io/master", Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoSchedule}, + {Key: "node-role.kubernetes.io/control-plane", Operator: v1.TolerationOpExists, Effect: v1.TaintEffectNoSchedule}, + }, + Containers: []v1.Container{{ + Name: "healthserver", + Image: image, + Args: func() []string { + args := []string{ + "serve", + fmt.Sprintf("--port=%d", healthserverPort), + fmt.Sprintf("--startup-delay=%s", startupDelay), + } + if len(aggregatorURL) > 0 && aggregatorURL[0] != "" { + args = append(args, fmt.Sprintf("--aggregator=%s", aggregatorURL[0])) + } + return args + }(), + Ports: []v1.ContainerPort{{ + Name: "http", + ContainerPort: healthserverPort, + HostPort: healthserverPort, + }}, + SecurityContext: &v1.SecurityContext{ + AllowPrivilegeEscalation: ptrBool(false), + Capabilities: &v1.Capabilities{ + Drop: []v1.Capability{"ALL"}, + }, + SeccompProfile: &v1.SeccompProfile{ + Type: v1.SeccompProfileTypeRuntimeDefault, + }, + }, + Env: []v1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.name"}, + }, + }, + { + Name: "POD_IP", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }, + }, + }}, + }, + }, + }, + } +} + +// waitForDaemonSetReady polls the DaemonSet status until NumberReady equals +// DesiredNumberScheduled (and DesiredNumberScheduled > 0), or the timeout +// is reached. Returns the DesiredNumberScheduled count. +func waitForDaemonSetReady(ctx context.Context, cs clientset.Interface, namespace, name string, timeout time.Duration) (int32, error) { + var desired int32 + err := wait.PollUntilContextTimeout(ctx, 5*time.Second, timeout, true, func(ctx context.Context) (bool, error) { + ds, err := cs.AppsV1().DaemonSets(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return false, nil + } + desired = ds.Status.DesiredNumberScheduled + framework.Logf("daemonset ready: %d/%d", ds.Status.NumberReady, desired) + return desired > 0 && ds.Status.NumberReady == desired, nil + }) + return desired, err +} + // buildHealthTransitionService creates a Service spec for an NLB that: // - Targets only master/control-plane nodes (target-node-labels annotation) // - Enables cross-zone load balancing for HA @@ -1816,7 +2894,7 @@ func deployAggregator(ctx context.Context, cs clientset.Interface, namespace, im // deployInClusterClient creates a Pod on a worker node that sends HTTP // requests to the NLB. Returns the pod name for result fetching. -func deployInClusterClient(ctx context.Context, cs clientset.Interface, namespace, image, nlbDNS, aggregatorURL string) string { +func deployInClusterClient(ctx context.Context, cs clientset.Interface, namespace, image, nlbDNS, aggregatorURL string, workers int, interval time.Duration) string { podName := "healthtest-client" pod := &v1.Pod{ @@ -1850,14 +2928,14 @@ func deployInClusterClient(ctx context.Context, cs clientset.Interface, namespac FieldRef: &v1.ObjectFieldSelector{FieldPath: "status.podIP"}, }, }}, - Args: []string{ - "client", - fmt.Sprintf("--url=http://%s:%d/", nlbDNS, healthserverPort), - fmt.Sprintf("--workers=%d", defaultClientWorkers), - fmt.Sprintf("--interval=%s", defaultClientInterval), - fmt.Sprintf("--port=%d", clientPort), - fmt.Sprintf("--aggregator=%s", aggregatorURL), - }, + Args: []string{ + "client", + fmt.Sprintf("--url=http://%s:%d/", nlbDNS, healthserverPort), + fmt.Sprintf("--workers=%d", workers), + fmt.Sprintf("--interval=%s", interval), + fmt.Sprintf("--port=%d", clientPort), + fmt.Sprintf("--aggregator=%s", aggregatorURL), + }, Ports: []v1.ContainerPort{{ Name: "http", ContainerPort: int32(clientPort), @@ -1896,6 +2974,110 @@ func deployInClusterClient(ctx context.Context, cs clientset.Interface, namespac return podName } +// deployClientDaemonSet creates a DaemonSet of client pods, one per worker +// node. Because each pod has a different source IP, the NLB distributes +// traffic across all targets even with preserve_client_ip.enabled=true. +// Returns the list of pod names created by the DaemonSet. +func deployClientDaemonSet(ctx context.Context, cs clientset.Interface, namespace, image, nlbDNS, aggregatorURL string, workers int, interval time.Duration) []string { + dsName := "healthtest-client" + labels := map[string]string{"app": dsName} + + ds := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{Name: dsName, Namespace: namespace}, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{Labels: labels}, + Spec: v1.PodSpec{ + // Worker nodes only — do not land on control-plane. + NodeSelector: map[string]string{"node-role.kubernetes.io/worker": ""}, + Containers: []v1.Container{{ + Name: "client", + Image: image, + Env: []v1.EnvVar{{ + Name: "POD_IP", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }}, + Args: []string{ + "client", + fmt.Sprintf("--url=http://%s:%d/", nlbDNS, healthserverPort), + fmt.Sprintf("--workers=%d", workers), + fmt.Sprintf("--interval=%s", interval), + fmt.Sprintf("--port=%d", clientPort), + fmt.Sprintf("--aggregator=%s", aggregatorURL), + }, + Ports: []v1.ContainerPort{{ + Name: "http", + ContainerPort: int32(clientPort), + }}, + ReadinessProbe: &v1.Probe{ + ProbeHandler: v1.ProbeHandler{ + HTTPGet: &v1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt(clientPort), + }, + }, + PeriodSeconds: 2, + }, + }}, + }, + }, + }, + } + + _, err := cs.AppsV1().DaemonSets(namespace).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create client DaemonSet") + + // Wait for all pods ready. + var podNames []string + err = wait.PollUntilContextTimeout(ctx, 3*time.Second, 3*time.Minute, true, func(ctx context.Context) (bool, error) { + d, err := cs.AppsV1().DaemonSets(namespace).Get(ctx, dsName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + framework.Logf("[client-ds] ready: %d/%d", d.Status.NumberReady, d.Status.DesiredNumberScheduled) + if d.Status.DesiredNumberScheduled == 0 || d.Status.NumberReady < d.Status.DesiredNumberScheduled { + return false, nil + } + // Collect pod names. + pods, err := cs.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", dsName), + }) + if err != nil { + return false, nil + } + podNames = nil + for _, p := range pods.Items { + if p.DeletionTimestamp == nil { + podNames = append(podNames, p.Name) + } + } + return true, nil + }) + framework.ExpectNoError(err, "client DaemonSet ready") + framework.Logf("[client-ds] %d client pods ready on worker nodes: %v", len(podNames), podNames) + return podNames +} + +// fetchMergedClientRecords fetches records from all client pods and merges +// them into a single slice sorted by timestamp. Use this when multiple client +// pods (DaemonSet) are deployed; each pod tracks only its own requests. +func fetchMergedClientRecords(ctx context.Context, cs clientset.Interface, namespace string, podNames []string) []health.RequestRecord { + var merged []health.RequestRecord + for _, pod := range podNames { + recs := fetchClientRecords(ctx, cs, namespace, pod) + framework.Logf("[client-ds] fetched %d records from %s", len(recs), pod) + merged = append(merged, recs...) + } + sort.Slice(merged, func(i, j int) bool { + return merged[i].Timestamp.Before(merged[j].Timestamp) + }) + framework.Logf("[client-ds] merged %d total records from %d pods", len(merged), len(podNames)) + return merged +} + // fetchClientRecords retrieves all request records from the in-cluster // client pod via the K8s API server proxy. The client pod runs on a worker // node with normal networking, so the API proxy works. @@ -2120,7 +3302,8 @@ func setupHealthTransitionCLB( // Deploy in-cluster client By("deploying in-cluster client on worker node") - clientPodName = deployInClusterClient(ctx, cs, ns.Name, image, lbDNS, aggregatorURL) + clientPodName = deployInClusterClient(ctx, cs, ns.Name, image, lbDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval) return lbDNS, clbObserver, cfg, setupTimes, clientPodName } diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go b/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go new file mode 100644 index 000000000..34aa4bcd6 --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go @@ -0,0 +1,606 @@ +package aws + +import ( + "context" + "fmt" + "strings" + "time" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + ec2 "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" + elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" + "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" +) + +// SDKManagedNLB holds all the AWS resources created for an SDK-managed NLB. +// Used for cleanup via DeferCleanup. +type SDKManagedNLB struct { + NLBARN string + NLBDNS string + TGARN string + ListenerARN string + NLBSGID string // SG created for and attached to the NLB (for cleanup) + SGID string // Master SG that was modified (rule added) + SGRuleID string // ID of the ingress rule we added on master SG (for cleanup) + VPC string + Subnets []string + InstanceIDs []string + Port int32 + InfraID string +} + +// ClusterInfra holds the discovered cluster infrastructure details needed to +// create an SDK-managed NLB that mirrors how the OCP installer provisions +// the KAS NLB. +type ClusterInfra struct { + InfraID string + VPCID string + SubnetIDs []string + InstanceIDs []string + MasterSGID string +} + +// discoverClusterInfra discovers VPC, subnets, master instance IDs, and master +// security group from the running cluster. Instance IDs come from K8s node +// spec.providerID (reliable, no EC2 tag assumptions). VPC, subnets, and SG +// come from EC2 DescribeInstances using those known instance IDs. +func discoverClusterInfra(ctx context.Context, cs clientset.Interface, ec2Client *ec2.Client) (*ClusterInfra, error) { + // Get the infrastructure name from the Infrastructure CR. + ocClient, err := common.GetOcClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create config client: %w", err) + } + infra, err := ocClient.Infrastructures().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get Infrastructure CR: %w", err) + } + infraID := infra.Status.InfrastructureName + if infraID == "" { + return nil, fmt.Errorf("Infrastructure status.infrastructureName is empty") + } + framework.Logf("discovered infrastructure name: %s", infraID) + + // Get control-plane node instance IDs from K8s node spec.providerID. + // providerID format: aws:///us-east-1a/i-0123456789abcdef0 + nodes, err := cs.CoreV1().Nodes().List(ctx, metav1.ListOptions{ + LabelSelector: "node-role.kubernetes.io/control-plane", + }) + if err != nil { + return nil, fmt.Errorf("failed to list control-plane nodes: %w", err) + } + if len(nodes.Items) == 0 { + return nil, fmt.Errorf("no control-plane nodes found") + } + + var instanceIDs []string + for _, node := range nodes.Items { + // Parse providerID: aws:///us-east-1a/i-xxxx → extract i-xxxx + providerID := node.Spec.ProviderID + parts := strings.Split(providerID, "/") + if len(parts) < 2 { + framework.Logf("skipping node %s with unexpected providerID: %s", node.Name, providerID) + continue + } + instanceID := parts[len(parts)-1] + if !strings.HasPrefix(instanceID, "i-") { + framework.Logf("skipping node %s with non-EC2 instance ID: %s", node.Name, instanceID) + continue + } + instanceIDs = append(instanceIDs, instanceID) + framework.Logf("node %s → instance %s", node.Name, instanceID) + } + if len(instanceIDs) == 0 { + return nil, fmt.Errorf("no EC2 instance IDs found from control-plane nodes") + } + + // Use EC2 DescribeInstances with known instance IDs to get VPC, subnets, SG. + result, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ + InstanceIds: instanceIDs, + }) + if err != nil { + return nil, fmt.Errorf("failed to describe instances %v: %w", instanceIDs, err) + } + + ci := &ClusterInfra{ + InfraID: infraID, + InstanceIDs: instanceIDs, + } + + seenSubnets := make(map[string]bool) + for _, reservation := range result.Reservations { + for _, inst := range reservation.Instances { + // Capture VPC from the first instance. + if ci.VPCID == "" { + ci.VPCID = awssdk.ToString(inst.VpcId) + framework.Logf("discovered VPC: %s", ci.VPCID) + } + + // Collect unique subnets. + subnetID := awssdk.ToString(inst.SubnetId) + if !seenSubnets[subnetID] { + seenSubnets[subnetID] = true + ci.SubnetIDs = append(ci.SubnetIDs, subnetID) + } + + // Find the master security group. + if ci.MasterSGID == "" { + for _, sg := range inst.SecurityGroups { + sgName := awssdk.ToString(sg.GroupName) + if strings.Contains(sgName, "-master-sg") || strings.Contains(sgName, "-controlplane") { + ci.MasterSGID = awssdk.ToString(sg.GroupId) + framework.Logf("discovered master SG: %s (%s)", ci.MasterSGID, sgName) + break + } + } + // Fallback: use the first SG from the first master instance. + if ci.MasterSGID == "" && len(inst.SecurityGroups) > 0 { + ci.MasterSGID = awssdk.ToString(inst.SecurityGroups[0].GroupId) + framework.Logf("using first SG as master SG (fallback): %s", ci.MasterSGID) + } + } + } + } + + framework.Logf("discovered %d master instances: %v", len(ci.InstanceIDs), ci.InstanceIDs) + framework.Logf("discovered %d subnets: %v", len(ci.SubnetIDs), ci.SubnetIDs) + + return ci, nil +} + +// addSGIngressRule adds an inbound TCP rule on the given port from 0.0.0.0/0 +// to the specified security group. It returns the security group rule ID for +// later cleanup. +func addSGIngressRule(ctx context.Context, ec2Client *ec2.Client, sgID string, port int32) (string, error) { + framework.Logf("adding ingress rule to SG %s: TCP port %d from 0.0.0.0/0", sgID, port) + + input := &ec2.AuthorizeSecurityGroupIngressInput{ + GroupId: awssdk.String(sgID), + IpPermissions: []ec2types.IpPermission{ + { + IpProtocol: awssdk.String("tcp"), + FromPort: awssdk.Int32(port), + ToPort: awssdk.Int32(port), + IpRanges: []ec2types.IpRange{ + { + CidrIp: awssdk.String("0.0.0.0/0"), + Description: awssdk.String(fmt.Sprintf("e2e-nlb-health-test port %d", port)), + }, + }, + }, + }, + } + + result, err := ec2Client.AuthorizeSecurityGroupIngress(ctx, input) + if err != nil { + // Handle idempotency: if the rule already exists, find its ID + // so we can still clean it up later. + if strings.Contains(err.Error(), "InvalidPermission.Duplicate") { + framework.Logf("SG rule for TCP %d already exists on %s, finding existing rule ID", port, sgID) + ruleID, findErr := findSGRuleID(ctx, ec2Client, sgID, port) + if findErr != nil { + framework.Logf("warning: could not find existing rule ID: %v", findErr) + return "", nil // Rule exists but we can't find the ID — skip cleanup + } + framework.Logf("found existing ingress rule %s on SG %s for TCP port %d", ruleID, sgID, port) + return ruleID, nil + } + return "", fmt.Errorf("failed to add ingress rule to SG %s: %w", sgID, err) + } + + var ruleID string + if len(result.SecurityGroupRules) > 0 { + ruleID = awssdk.ToString(result.SecurityGroupRules[0].SecurityGroupRuleId) + } + framework.Logf("added ingress rule %s to SG %s for TCP port %d", ruleID, sgID, port) + return ruleID, nil +} + +// findSGRuleID finds the rule ID of an existing inbound TCP rule on the +// given port. Used when the rule already exists (idempotent add). +func findSGRuleID(ctx context.Context, ec2Client *ec2.Client, sgID string, port int32) (string, error) { + output, err := ec2Client.DescribeSecurityGroupRules(ctx, &ec2.DescribeSecurityGroupRulesInput{ + Filters: []ec2types.Filter{ + {Name: awssdk.String("group-id"), Values: []string{sgID}}, + }, + }) + if err != nil { + return "", err + } + for _, rule := range output.SecurityGroupRules { + if !awssdk.ToBool(rule.IsEgress) && + awssdk.ToString(rule.IpProtocol) == "tcp" && + awssdk.ToInt32(rule.FromPort) == port && + awssdk.ToInt32(rule.ToPort) == port { + return awssdk.ToString(rule.SecurityGroupRuleId), nil + } + } + return "", fmt.Errorf("no matching rule found for TCP %d on SG %s", port, sgID) +} + +// removeSGIngressRule removes the specified inbound rule from the security +// group. Best-effort: errors are logged but not returned. +func removeSGIngressRule(ctx context.Context, ec2Client *ec2.Client, sgID, ruleID string) { + framework.Logf("removing ingress rule %s from SG %s", ruleID, sgID) + + input := &ec2.RevokeSecurityGroupIngressInput{ + GroupId: awssdk.String(sgID), + SecurityGroupRuleIds: []string{ruleID}, + } + + _, err := ec2Client.RevokeSecurityGroupIngress(ctx, input) + if err != nil { + framework.Logf("WARNING: failed to remove ingress rule %s from SG %s (best-effort): %v", ruleID, sgID, err) + return + } + framework.Logf("removed ingress rule %s from SG %s", ruleID, sgID) +} + +// createSDKManagedNLB creates an NLB, target group, and listener via the AWS +// SDK, replicating how the OCP installer provisions the KAS NLB. +func createSDKManagedNLB(ctx context.Context, elbClient *elbv2.Client, ec2Client *ec2.Client, infra *ClusterInfra, port int32) (*SDKManagedNLB, error) { + // Use the infra ID truncated to fit AWS 32-char name limit. + // Strip trailing dashes to satisfy AWS naming regex: (?!.*-$)^[A-Za-z0-9-]+$ + shortID := infra.InfraID + if len(shortID) > 16 { + shortID = shortID[:16] + } + shortID = strings.TrimRight(shortID, "-") + resourceName := fmt.Sprintf("e2e-ht-%s", shortID) + + nlb := &SDKManagedNLB{ + VPC: infra.VPCID, + Subnets: infra.SubnetIDs, + InstanceIDs: infra.InstanceIDs, + Port: port, + InfraID: infra.InfraID, + } + + // 1. Create target group. + framework.Logf("creating target group %s (port %d)", resourceName, port) + tgInput := &elbv2.CreateTargetGroupInput{ + Name: awssdk.String(resourceName), + TargetType: elbv2types.TargetTypeEnumInstance, + Protocol: elbv2types.ProtocolEnumTcp, + Port: awssdk.Int32(port), + VpcId: awssdk.String(infra.VPCID), + HealthCheckEnabled: awssdk.Bool(true), + HealthCheckProtocol: elbv2types.ProtocolEnumHttp, + HealthCheckPath: awssdk.String("/readyz"), + HealthCheckPort: awssdk.String(fmt.Sprintf("%d", port)), + HealthCheckIntervalSeconds: awssdk.Int32(10), + HealthyThresholdCount: awssdk.Int32(2), + UnhealthyThresholdCount: awssdk.Int32(2), + } + + tgResult, err := elbClient.CreateTargetGroup(ctx, tgInput) + if err != nil { + return nlb, fmt.Errorf("failed to create target group: %w", err) + } + nlb.TGARN = awssdk.ToString(tgResult.TargetGroups[0].TargetGroupArn) + framework.Logf("created target group: %s", nlb.TGARN) + + // 2. Register targets. + targets := make([]elbv2types.TargetDescription, 0, len(infra.InstanceIDs)) + for _, id := range infra.InstanceIDs { + targets = append(targets, elbv2types.TargetDescription{ + Id: awssdk.String(id), + Port: awssdk.Int32(port), + }) + } + + regInput := &elbv2.RegisterTargetsInput{ + TargetGroupArn: awssdk.String(nlb.TGARN), + Targets: targets, + } + _, err = elbClient.RegisterTargets(ctx, regInput) + if err != nil { + return nlb, fmt.Errorf("failed to register targets: %w", err) + } + framework.Logf("registered %d targets in target group", len(targets)) + + // 3. Create NLB security group (allows inbound TCP on the test port). + // Idempotent: if the SG already exists (e.g. leftover from a previous failed + // run that couldn't delete it due to DependencyViolation), look it up and + // reuse it rather than failing. + nlbSGName := resourceName + "-nlb-sg" + framework.Logf("creating NLB security group %s in VPC %s", nlbSGName, infra.VPCID) + sgResult, err := ec2Client.CreateSecurityGroup(ctx, &ec2.CreateSecurityGroupInput{ + GroupName: awssdk.String(nlbSGName), + Description: awssdk.String(fmt.Sprintf("e2e NLB SG for port %d", port)), + VpcId: awssdk.String(infra.VPCID), + TagSpecifications: []ec2types.TagSpecification{{ + ResourceType: ec2types.ResourceTypeSecurityGroup, + Tags: []ec2types.Tag{ + {Key: awssdk.String("kubernetes.io/cluster/" + infra.InfraID), Value: awssdk.String("owned")}, + {Key: awssdk.String("e2e-test"), Value: awssdk.String("health-transition")}, + }, + }}, + }) + if err != nil { + if !strings.Contains(err.Error(), "InvalidGroup.Duplicate") { + return nlb, fmt.Errorf("failed to create NLB security group: %w", err) + } + // SG left over from a previous run — look it up by name. + framework.Logf("NLB SG %s already exists (leftover), looking up existing SG ID", nlbSGName) + descResult, descErr := ec2Client.DescribeSecurityGroups(ctx, &ec2.DescribeSecurityGroupsInput{ + Filters: []ec2types.Filter{ + {Name: awssdk.String("group-name"), Values: []string{nlbSGName}}, + {Name: awssdk.String("vpc-id"), Values: []string{infra.VPCID}}, + }, + }) + if descErr != nil || len(descResult.SecurityGroups) == 0 { + return nlb, fmt.Errorf("NLB SG %s already exists but could not look it up: %w", nlbSGName, err) + } + nlb.NLBSGID = awssdk.ToString(descResult.SecurityGroups[0].GroupId) + framework.Logf("reusing existing NLB SG: %s", nlb.NLBSGID) + } else { + nlb.NLBSGID = awssdk.ToString(sgResult.GroupId) + framework.Logf("created NLB SG: %s", nlb.NLBSGID) + } + + // Allow inbound TCP on the test port. Idempotent: ignore duplicate rule errors. + _, err = ec2Client.AuthorizeSecurityGroupIngress(ctx, &ec2.AuthorizeSecurityGroupIngressInput{ + GroupId: awssdk.String(nlb.NLBSGID), + IpPermissions: []ec2types.IpPermission{{ + IpProtocol: awssdk.String("tcp"), + FromPort: awssdk.Int32(port), + ToPort: awssdk.Int32(port), + IpRanges: []ec2types.IpRange{{ + CidrIp: awssdk.String("0.0.0.0/0"), + Description: awssdk.String(fmt.Sprintf("e2e NLB inbound TCP %d", port)), + }}, + }}, + }) + if err != nil && !strings.Contains(err.Error(), "InvalidPermission.Duplicate") { + return nlb, fmt.Errorf("failed to add ingress rule to NLB SG: %w", err) + } + framework.Logf("added ingress rule to NLB SG %s: TCP %d from 0.0.0.0/0", nlb.NLBSGID, port) + + // 4. Create NLB (internal — matches KAS internal NLB and allows + // in-cluster clients to reach it via VPC-internal DNS). + framework.Logf("creating NLB %s (internal, subnets=%v)", resourceName, infra.SubnetIDs) + lbInput := &elbv2.CreateLoadBalancerInput{ + Name: awssdk.String(resourceName), + Type: elbv2types.LoadBalancerTypeEnumNetwork, + Scheme: elbv2types.LoadBalancerSchemeEnumInternal, + Subnets: infra.SubnetIDs, + SecurityGroups: []string{nlb.NLBSGID}, + Tags: []elbv2types.Tag{ + { + Key: awssdk.String("kubernetes.io/cluster/" + infra.InfraID), + Value: awssdk.String("owned"), + }, + { + Key: awssdk.String("e2e-test"), + Value: awssdk.String("health-transition"), + }, + }, + } + + lbResult, err := elbClient.CreateLoadBalancer(ctx, lbInput) + if err != nil { + return nlb, fmt.Errorf("failed to create NLB: %w", err) + } + nlb.NLBARN = awssdk.ToString(lbResult.LoadBalancers[0].LoadBalancerArn) + nlb.NLBDNS = awssdk.ToString(lbResult.LoadBalancers[0].DNSName) + framework.Logf("created NLB: ARN=%s DNS=%s", nlb.NLBARN, nlb.NLBDNS) + + // 4. Wait for NLB to become active. + if err := waitForNLBActive(ctx, elbClient, nlb.NLBARN, 5*time.Minute); err != nil { + return nlb, fmt.Errorf("NLB did not become active: %w", err) + } + + // 5. Create listener. + framework.Logf("creating listener on NLB (TCP port %d -> TG %s)", port, nlb.TGARN) + listenerInput := &elbv2.CreateListenerInput{ + LoadBalancerArn: awssdk.String(nlb.NLBARN), + Protocol: elbv2types.ProtocolEnumTcp, + Port: awssdk.Int32(port), + DefaultActions: []elbv2types.Action{ + { + Type: elbv2types.ActionTypeEnumForward, + TargetGroupArn: awssdk.String(nlb.TGARN), + }, + }, + } + + listenerResult, err := elbClient.CreateListener(ctx, listenerInput) + if err != nil { + return nlb, fmt.Errorf("failed to create listener: %w", err) + } + nlb.ListenerARN = awssdk.ToString(listenerResult.Listeners[0].ListenerArn) + framework.Logf("created listener: %s", nlb.ListenerARN) + + return nlb, nil +} + +// deleteSDKManagedNLB tears down all resources created by createSDKManagedNLB +// in the correct order. Best-effort: errors are logged but do not fail the test. +func deleteSDKManagedNLB(ctx context.Context, elbClient *elbv2.Client, ec2Client *ec2.Client, nlb *SDKManagedNLB) { + // 1. Delete listener. + if nlb.ListenerARN != "" { + framework.Logf("deleting listener %s", nlb.ListenerARN) + _, err := elbClient.DeleteListener(ctx, &elbv2.DeleteListenerInput{ + ListenerArn: awssdk.String(nlb.ListenerARN), + }) + if err != nil { + framework.Logf("WARNING: failed to delete listener %s (best-effort): %v", nlb.ListenerARN, err) + } else { + framework.Logf("deleted listener %s", nlb.ListenerARN) + } + } + + // 2. Delete NLB. + if nlb.NLBARN != "" { + framework.Logf("deleting NLB %s", nlb.NLBARN) + _, err := elbClient.DeleteLoadBalancer(ctx, &elbv2.DeleteLoadBalancerInput{ + LoadBalancerArn: awssdk.String(nlb.NLBARN), + }) + if err != nil { + framework.Logf("WARNING: failed to delete NLB %s (best-effort): %v", nlb.NLBARN, err) + } else { + framework.Logf("deleted NLB %s", nlb.NLBARN) + } + + // 3. Wait for NLB deletion to complete. + waitForNLBDeleted(ctx, elbClient, nlb.NLBARN, 5*time.Minute) + } + + // 4. Deregister targets. + if nlb.TGARN != "" { + targets := make([]elbv2types.TargetDescription, 0, len(nlb.InstanceIDs)) + for _, id := range nlb.InstanceIDs { + targets = append(targets, elbv2types.TargetDescription{ + Id: awssdk.String(id), + Port: awssdk.Int32(nlb.Port), + }) + } + if len(targets) > 0 { + framework.Logf("deregistering %d targets from TG %s", len(targets), nlb.TGARN) + _, err := elbClient.DeregisterTargets(ctx, &elbv2.DeregisterTargetsInput{ + TargetGroupArn: awssdk.String(nlb.TGARN), + Targets: targets, + }) + if err != nil { + framework.Logf("WARNING: failed to deregister targets from TG %s (best-effort): %v", nlb.TGARN, err) + } else { + framework.Logf("deregistered targets from TG %s", nlb.TGARN) + } + } + + // 5. Delete target group. + framework.Logf("deleting target group %s", nlb.TGARN) + _, err := elbClient.DeleteTargetGroup(ctx, &elbv2.DeleteTargetGroupInput{ + TargetGroupArn: awssdk.String(nlb.TGARN), + }) + if err != nil { + framework.Logf("WARNING: failed to delete target group %s (best-effort): %v", nlb.TGARN, err) + } else { + framework.Logf("deleted target group %s", nlb.TGARN) + } + } + + // 6. Delete the NLB security group created by createSDKManagedNLB. + // AWS releases the SG dependency asynchronously after NLB deletion, so + // retry with backoff on DependencyViolation errors. + if nlb.NLBSGID != "" && ec2Client != nil { + framework.Logf("deleting NLB security group %s (with retry for dependency release)", nlb.NLBSGID) + retryDelays := []int{5, 10, 15, 20, 30} + for attempt, delaySec := range retryDelays { + _, err := ec2Client.DeleteSecurityGroup(ctx, &ec2.DeleteSecurityGroupInput{ + GroupId: awssdk.String(nlb.NLBSGID), + }) + if err == nil { + framework.Logf("deleted NLB security group %s (attempt %d)", nlb.NLBSGID, attempt+1) + break + } + if strings.Contains(err.Error(), "DependencyViolation") && attempt < len(retryDelays)-1 { + framework.Logf("SG %s still has dependents (attempt %d), retrying in %ds: %v", + nlb.NLBSGID, attempt+1, delaySec, err) + time.Sleep(time.Duration(delaySec) * time.Second) + continue + } + framework.Logf("WARNING: failed to delete NLB SG %s after %d attempts (best-effort): %v", + nlb.NLBSGID, attempt+1, err) + break + } + } +} + +// waitForNLBActive polls DescribeLoadBalancers until the NLB state is "active" +// or the timeout is reached. +func waitForNLBActive(ctx context.Context, elbClient *elbv2.Client, nlbARN string, timeout time.Duration) error { + framework.Logf("waiting for NLB %s to become active (timeout %s)", nlbARN, timeout) + + deadline := time.Now().Add(timeout) + for { + if time.Now().After(deadline) { + return fmt.Errorf("timed out waiting for NLB %s to become active after %s", nlbARN, timeout) + } + + result, err := elbClient.DescribeLoadBalancers(ctx, &elbv2.DescribeLoadBalancersInput{ + LoadBalancerArns: []string{nlbARN}, + }) + if err != nil { + framework.Logf("transient error describing NLB %s (will retry): %v", nlbARN, err) + time.Sleep(10 * time.Second) + continue + } + + if len(result.LoadBalancers) > 0 { + state := result.LoadBalancers[0].State + if state != nil { + framework.Logf("NLB %s state: %s", nlbARN, state.Code) + if state.Code == elbv2types.LoadBalancerStateEnumActive { + framework.Logf("NLB %s is active", nlbARN) + return nil + } + } + } + + time.Sleep(10 * time.Second) + } +} + +// waitForNLBDeleted polls DescribeLoadBalancers until the NLB is gone (404) +// or the timeout is reached. Best-effort: errors are logged but not returned. +func waitForNLBDeleted(ctx context.Context, elbClient *elbv2.Client, nlbARN string, timeout time.Duration) { + framework.Logf("waiting for NLB %s to be deleted (timeout %s)", nlbARN, timeout) + + deadline := time.Now().Add(timeout) + for { + if time.Now().After(deadline) { + framework.Logf("WARNING: timed out waiting for NLB %s deletion after %s", nlbARN, timeout) + return + } + + result, err := elbClient.DescribeLoadBalancers(ctx, &elbv2.DescribeLoadBalancersInput{ + LoadBalancerArns: []string{nlbARN}, + }) + if err != nil { + // A "not found" error means the NLB has been deleted. + if strings.Contains(err.Error(), "LoadBalancerNotFound") { + framework.Logf("NLB %s has been deleted", nlbARN) + return + } + framework.Logf("transient error describing NLB %s (will retry): %v", nlbARN, err) + time.Sleep(10 * time.Second) + continue + } + + if len(result.LoadBalancers) == 0 { + framework.Logf("NLB %s has been deleted", nlbARN) + return + } + + framework.Logf("NLB %s still exists, waiting for deletion...", nlbARN) + time.Sleep(10 * time.Second) + } +} + +// setTGPreserveClientIP sets the preserve_client_ip.enabled attribute on a +// target group. Pass enabled=false to disable source-IP stickiness so the NLB +// distributes connections across targets independent of the client IP. +func setTGPreserveClientIP(ctx context.Context, elbClient *elbv2.Client, tgARN string, enabled bool) error { + val := "true" + if !enabled { + val = "false" + } + framework.Logf("setting TG %s preserve_client_ip.enabled=%s", tgARN, val) + _, err := elbClient.ModifyTargetGroupAttributes(ctx, &elbv2.ModifyTargetGroupAttributesInput{ + TargetGroupArn: awssdk.String(tgARN), + Attributes: []elbv2types.TargetGroupAttribute{ + {Key: awssdk.String("preserve_client_ip.enabled"), Value: awssdk.String(val)}, + }, + }) + if err != nil { + return fmt.Errorf("failed to set preserve_client_ip.enabled=%s on TG %s: %w", val, tgARN, err) + } + framework.Logf("TG %s preserve_client_ip.enabled=%s set", tgARN, val) + return err +} From def241a60afe41b085cedacc34c0f5807e1c91d5 Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Thu, 13 Aug 2026 21:24:19 -0300 Subject: [PATCH 21/22] e2e: add KAS-config SDK variants with real TG draining attributes Add two new SDK multi-client test variants (5.5-SDK-multi-kas and 5.5-SDK-multi-kas-cip) that configure TG with real KAS NLB attributes: connection_termination=false, draining_interval=300s. This matches production behaviour where the NLB drains unhealthy targets for up to 300s instead of immediately terminating connections (AWS default). Add setTGKASAttributes() in sdk_nlb.go to apply all six KAS TG attributes in a single ModifyTargetGroupAttributes call. Update TEST_CASES.md with scenario docs, comparison matrix, and real KAS attribute reference. Co-authored-by: Cursor --- .../e2e/aws/health/TEST_CASES.md | 123 ++++- .../e2e/aws/lb_health_transition.go | 449 ++++++++++++++++++ .../ccm-aws-tests/e2e/aws/sdk_nlb.go | 40 ++ 3 files changed, 593 insertions(+), 19 deletions(-) diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md index b4665399f..e1c951475 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md @@ -268,18 +268,101 @@ $BIN run-test "...multi-client preserve_client_ip=false..." --- +## Scenario 5.5-SDK-multi-kas — Multi-Client, Real KAS TG Config + +**Report label:** `5.5-SDK-multi-kas (Multi-Client + KAS TG Config / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing, multi-client KAS-config (OCPBUGS-86789)` + +Multi-client DaemonSet with TG attributes **exactly matching the real KAS NLB**. +This is the most faithful reproduction of real OCPBUGS-86789 conditions. + +| Parameter | Value | +|-----------|-------| +| Client | DaemonSet on workers, 16 workers × 50ms per pod | +| preserve_client_ip | **false** (matches real KAS) | +| connection_termination | **false** (matches real KAS; default is true) | +| draining_interval | **300s** (matches real KAS; default is 0) | +| deregistration_delay | 300s | +| deregistration_delay.connection_termination | false | +| stickiness | false | + +**Key difference from 5.5-SDK-multi-no-cip:** The `connection_termination=false` + +`draining_interval=300s` combination means the NLB does NOT immediately terminate +connections to unhealthy targets. Instead, it drains them for up to 300s — the same +window as the real KAS NLB. This produces higher `Unhealthy_reqs` counts in the +GracefulShutdown phase, matching production behaviour. + +**Run:** +```bash +$BIN run-test "...multi-client KAS-config..." +``` + +**Code:** `lb_health_transition.go`, `setTGKASAttributes()` in `sdk_nlb.go` + +--- + +## Scenario 5.5-SDK-multi-kas-cip — Multi-Client, KAS TG Config + CIP + +**Report label:** `5.5-SDK-multi-kas-cip (Multi-Client + KAS TG Config + CIP / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing, multi-client KAS-config preserve_client_ip=true (OCPBUGS-86789)` + +Same as **5.5-SDK-multi-kas** but with `preserve_client_ip=true`. Allows isolating +the source-IP stickiness effect under real KAS draining / connection-termination +settings. + +| Parameter | Value | +|-----------|-------| +| Client | DaemonSet on workers, 16 workers × 50ms per pod | +| preserve_client_ip | **true** (override from KAS default) | +| connection_termination | **false** (matches real KAS) | +| draining_interval | **300s** (matches real KAS) | +| deregistration_delay | 300s | + +**Purpose:** Compare with 5.5-SDK-multi-kas to isolate `preserve_client_ip` effect +under production-identical draining settings. + +**Run:** +```bash +$BIN run-test "...multi-client KAS-config preserve_client_ip=true..." +``` + +**Code:** `lb_health_transition.go`, `setTGKASAttributes()` in `sdk_nlb.go` + +--- + ## SDK Variants — Comparison Matrix -All four share: healthserver DaemonSet on masters, SDK-managed NLB, same HC/TG config -(HTTP `/readyz`, interval=10s, threshold=2), same rollout simulation (delete one pod, -wait for same-node replacement). **Only client topology and `preserve_client_ip` differ.** +All SDK variants share: healthserver DaemonSet on masters, SDK-managed NLB, same +HC config (HTTP `/readyz`, interval=10s, threshold=2), same rollout simulation +(delete one pod, wait for same-node replacement). + +### Default TG attributes (5.5-SDK through 5.5-SDK-multi-no-cip) + +| Scenario | Client | preserve_client_ip | conn_term | draining | KAS-faithful | +|----------|--------|-------------------|-----------|----------|--------------| +| 5.5-SDK | 1 pod, 32w | true | true (default) | 0 (default) | NLB yes, clients no | +| 5.5-SDK-no-cip | 1 pod, 32w | false | true (default) | 0 (default) | NLB no | +| 5.5-SDK-multi | DS/worker, 16w | true | true (default) | 0 (default) | Clients yes, TG no | +| 5.5-SDK-multi-no-cip | DS/worker, 16w | false | true (default) | 0 (default) | Clients yes, TG no | -| Scenario | Client | preserve_client_ip | Traffic spread | KAS-faithful | -|----------|--------|-------------------|----------------|--------------| -| 5.5-SDK | 1 pod, 32w | true | Skewed (~1 target) | NLB yes, clients no | -| 5.5-SDK-no-cip | 1 pod, 32w | false | Even | NLB no | -| 5.5-SDK-multi | DS/worker, 16w | true | Even | **Yes (recommended)** | -| 5.5-SDK-multi-no-cip | DS/worker, 16w | false | Even | Clients no | +### Real KAS TG attributes (5.5-SDK-multi-kas, 5.5-SDK-multi-kas-cip) + +| Scenario | Client | preserve_client_ip | conn_term | draining | KAS-faithful | +|----------|--------|-------------------|-----------|----------|--------------| +| 5.5-SDK-multi-kas | DS/worker, 16w | false | **false** | **300s** | **Yes (most faithful)** | +| 5.5-SDK-multi-kas-cip | DS/worker, 16w | true | **false** | **300s** | CIP comparison | + +**Real KAS TG attributes** (from `aws elbv2 describe-target-group-attributes`): +``` +preserve_client_ip.enabled = false +target_health_state.unhealthy.connection_termination.enabled = false +target_health_state.unhealthy.draining_interval_seconds = 300 +deregistration_delay.timeout_seconds = 300 +deregistration_delay.connection_termination.enabled = false +stickiness.enabled = false +``` **Shared AWS lifecycle** (all SDK variants): see v19 plan (`sdk_nlb.go`). Cleanup includes SG retry on `DependencyViolation` and idempotent SG create on reruns. @@ -348,16 +431,18 @@ pre-readyz routing is NLB-specific or general to all AWS LBs. ## Summary Table -| Scenario | LB Type | Managed by | Workload | Client | preserve_client_ip | Tests | -|----------|---------|------------|----------|--------|-------------------|-------| -| 5.5 | NLB | Kubernetes | Deployment | 1 pod | true (svc default) | Pre-readyz (OCPBUGS) | -| 5.5-CAPA | NLB | Kubernetes | Deployment | 1 pod | true | Pre-readyz + CAPA TG | -| 5.5-SDK | NLB | AWS SDK | DaemonSet | 1 pod, 32w | true | KAS-equivalent baseline | -| 5.5-SDK-no-cip | NLB | AWS SDK | DaemonSet | 1 pod, 32w | **false** | Stickiness isolation | -| 5.5-SDK-multi | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | **Recommended KAS-faithful** | -| 5.5-SDK-multi-no-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | Multi + no stickiness | -| 5.2 | NLB | Kubernetes | Deployment | 1 pod | true | Shutdown propagation (SPLAT-307) | -| 5.5-CLB | CLB | Kubernetes | Deployment | 1 pod | N/A | Pre-readyz CLB baseline | +| Scenario | LB Type | Managed by | Workload | Client | preserve_client_ip | conn_term / draining | Tests | +|----------|---------|------------|----------|--------|-------------------|---------------------|-------| +| 5.5 | NLB | Kubernetes | Deployment | 1 pod | true (svc default) | default | Pre-readyz (OCPBUGS) | +| 5.5-CAPA | NLB | Kubernetes | Deployment | 1 pod | true | default | Pre-readyz + CAPA TG | +| 5.5-SDK | NLB | AWS SDK | DaemonSet | 1 pod, 32w | true | default | KAS-equivalent baseline | +| 5.5-SDK-no-cip | NLB | AWS SDK | DaemonSet | 1 pod, 32w | **false** | default | Stickiness isolation | +| 5.5-SDK-multi | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | default | Multi-client baseline | +| 5.5-SDK-multi-no-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | default | Multi + no stickiness | +| 5.5-SDK-multi-kas | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | **false / 300s** | **Most faithful KAS repro** | +| 5.5-SDK-multi-kas-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | **false / 300s** | KAS TG + CIP comparison | +| 5.2 | NLB | Kubernetes | Deployment | 1 pod | true | default | Shutdown propagation (SPLAT-307) | +| 5.5-CLB | CLB | Kubernetes | Deployment | 1 pod | N/A | N/A | Pre-readyz CLB baseline | **Pass criteria (all 5.5* scenarios):** - `PreReadyzReqCount == 0` — no requests before `/readyz → 200` diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index 228da5a6a..a8fbbc35d 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -1549,6 +1549,455 @@ var _ = Describe(healthTransitionTestPrefix, func() { framework.Logf("\n%s", report) }) }) + + // ── Scenario 5.5-SDK-multi-kas ────────────────────────────────────── + // Multi-client DaemonSet with TG attributes matching the real KAS NLB: + // preserve_client_ip=false, connection_termination=false, + // draining_interval=300s, deregistration_delay=300s. + // This is the most faithful reproduction of real OCPBUGS-86789 conditions. + Context("SDK-managed NLB pre-readyz routing, multi-client KAS-config (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting, KAS TG attributes, and multiple client IPs", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + deployName := "healthserver" + clientDSName := "healthtest-client" + + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-multi-kas test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, clientDSName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + }) + + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + By("creating healthserver DaemonSet (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSet(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + By("creating SDK-managed NLB with instance:19443 targets") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort)) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + + By("applying KAS-equivalent TG attributes (conn_term=false, draining=300s, preserve_client_ip=false)") + err = setTGKASAttributes(ctx, elbClient, sdkNLB.TGARN, false) + framework.ExpectNoError(err, "set KAS TG attributes") + + observer := health.NewObserver(elbClient, 1*time.Second) + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + By("deploying client DaemonSet on worker nodes (one pod per worker)") + clientPodNames := deployClientDaemonSet(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval) + + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "client-mode": "multi-client-daemonset", + "preserve_client_ip": "false", + "connection_termination": "false", + "draining_interval": "300", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + defer func() { stopTGPush(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK-multi-kas (Multi-Client + KAS TG Config / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + report += buildVerdict55(tl, allRecords) + framework.Logf("\n%s", report) + }) + }) + + // ── Scenario 5.5-SDK-multi-kas-cip ────────────────────────────────── + // Same as 5.5-SDK-multi-kas but with preserve_client_ip=true. + // Allows comparing the effect of source-IP stickiness under real + // KAS draining/connection-termination settings. + Context("SDK-managed NLB pre-readyz routing, multi-client KAS-config preserve_client_ip=true (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting, KAS TG attributes, preserve_client_ip enabled, and multiple client IPs", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + deployName := "healthserver" + clientDSName := "healthtest-client" + + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-multi-kas-cip test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, clientDSName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + }) + + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + By("creating healthserver DaemonSet (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSet(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err := cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + By("creating SDK-managed NLB with instance:19443 targets") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort)) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + + By("applying KAS-equivalent TG attributes (conn_term=false, draining=300s, preserve_client_ip=true)") + err = setTGKASAttributes(ctx, elbClient, sdkNLB.TGARN, true) + framework.ExpectNoError(err, "set KAS TG attributes") + + observer := health.NewObserver(elbClient, 1*time.Second) + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + By("deploying client DaemonSet on worker nodes (one pod per worker)") + clientPodNames := deployClientDaemonSet(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval) + + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "client-mode": "multi-client-daemonset", + "preserve_client_ip": "true", + "connection_termination": "false", + "draining_interval": "300", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + defer func() { stopTGPush(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK-multi-kas-cip (Multi-Client + KAS TG Config + CIP / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + report += buildVerdict55(tl, allRecords) + framework.Logf("\n%s", report) + }) + }) }) // ─── Setup helper ─────────────────────────────────────────────────────────── diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go b/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go index 34aa4bcd6..837f57b19 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go @@ -604,3 +604,43 @@ func setTGPreserveClientIP(ctx context.Context, elbClient *elbv2.Client, tgARN s framework.Logf("TG %s preserve_client_ip.enabled=%s set", tgARN, val) return err } + +// setTGKASAttributes configures the target group to match the real KAS +// (kube-apiserver) NLB target group attributes. The key differences from +// AWS defaults are: +// - connection_termination=false: NLB does NOT immediately terminate +// connections to unhealthy targets (default: true). +// - draining_interval=300s: NLB drains unhealthy targets for up to 300s +// before stopping traffic (default: 0). +// - deregistration_delay=300s with connection_termination=false. +// - preserve_client_ip is configurable (KAS default: false). +func setTGKASAttributes(ctx context.Context, elbClient *elbv2.Client, tgARN string, preserveClientIP bool) error { + cipVal := "false" + if preserveClientIP { + cipVal = "true" + } + + attrs := []elbv2types.TargetGroupAttribute{ + {Key: awssdk.String("preserve_client_ip.enabled"), Value: awssdk.String(cipVal)}, + {Key: awssdk.String("target_health_state.unhealthy.connection_termination.enabled"), Value: awssdk.String("false")}, + {Key: awssdk.String("target_health_state.unhealthy.draining_interval_seconds"), Value: awssdk.String("300")}, + {Key: awssdk.String("deregistration_delay.timeout_seconds"), Value: awssdk.String("300")}, + {Key: awssdk.String("deregistration_delay.connection_termination.enabled"), Value: awssdk.String("false")}, + {Key: awssdk.String("stickiness.enabled"), Value: awssdk.String("false")}, + } + + framework.Logf("setting TG %s to KAS-equivalent attributes (preserve_client_ip=%s, conn_term=false, draining=300s)", tgARN, cipVal) + for _, a := range attrs { + framework.Logf(" %s=%s", *a.Key, *a.Value) + } + + _, err := elbClient.ModifyTargetGroupAttributes(ctx, &elbv2.ModifyTargetGroupAttributesInput{ + TargetGroupArn: awssdk.String(tgARN), + Attributes: attrs, + }) + if err != nil { + return fmt.Errorf("failed to set KAS attributes on TG %s: %w", tgARN, err) + } + framework.Logf("TG %s KAS attributes applied", tgARN) + return nil +} From 866f49d7e9a6786dd765e8fc7c617c69c98e8cac Mon Sep 17 00:00:00 2001 From: Marco Braga Date: Fri, 14 Aug 2026 00:04:06 -0300 Subject: [PATCH 22/22] e2e: add KAS TLS SDK variant and LateConnections shutdown metric Add 5.5-SDK-multi-kas-tls, a clone of the multi-client KAS-config test with TLS end-to-end on port 19443: healthserver serves traffic and /readyz via ListenAndServeTLS, the NLB uses HTTPS health checks on the same port, and clients connect with https:// plus --tls-insecure. Aggregator and client metrics stay on plain HTTP to avoid changing the existing observability path. Binary (e2e-nlb-health-test): - serve: --tls, --tls-cert, --tls-key for ListenAndServeTLS - client: --tls-insecure for self-signed NLB traffic Test helpers: - tls_certs.go: generate self-signed cert (wildcard SAN) and ConfigMap mount - buildHealthserverDaemonSetTLS(): mount cert and pass TLS flags - deployClientDaemonSet(..., useTLS): optional https URL and --tls-insecure - sdk_nlb.go: SDKNLBCreateOpts.HealthCheckProtocol for HTTPS HC Timeline/report (all 5.5* scenarios): - LateConnectionCount: requests to target after 80% of kasShutdownDelay - Timing table Late_conn_reqs and verdict [LATE-CONN] (informational) Document scenario in health/TEST_CASES.md. Co-authored-by: Cursor --- .../cmd/e2e-nlb-health-test/client.go | 12 +- .../cmd/e2e-nlb-health-test/serve.go | 15 + .../e2e/aws/health/TEST_CASES.md | 70 +++- .../e2e/aws/lb_health_transition.go | 302 +++++++++++++++++- .../ccm-aws-tests/e2e/aws/sdk_nlb.go | 16 +- .../ccm-aws-tests/e2e/aws/tls_certs.go | 101 ++++++ 6 files changed, 481 insertions(+), 35 deletions(-) create mode 100644 openshift-tests/ccm-aws-tests/e2e/aws/tls_certs.go diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go index 252ad4757..178948633 100644 --- a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/client.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "crypto/tls" "encoding/json" "flag" "fmt" @@ -24,6 +25,7 @@ func runClient(args []string) { workers := fs.Int("workers", 8, "parallel request goroutines") port := fs.Int("port", 8080, "port to serve metrics/records API") aggregatorURL := fs.String("aggregator", "", "aggregator URL for pushing events") + tlsInsecure := fs.Bool("tls-insecure", false, "skip TLS certificate verification for --url") fs.Parse(args) if *url == "" { @@ -84,11 +86,13 @@ func runClient(args []string) { stopCh := make(chan struct{}) // HTTP client that creates a new TCP connection for every request. + transport := &http.Transport{DisableKeepAlives: true} + if *tlsInsecure { + transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // test-only self-signed cert + } httpClient := &http.Client{ - Transport: &http.Transport{ - DisableKeepAlives: true, - }, - Timeout: 10 * time.Second, + Transport: transport, + Timeout: 10 * time.Second, } // sendRequest performs a single GET to the NLB URL with connection tracing. diff --git a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go index 911f43a01..7cb533d72 100644 --- a/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go +++ b/openshift-tests/ccm-aws-tests/cmd/e2e-nlb-health-test/serve.go @@ -20,8 +20,16 @@ func runServe(args []string) { port := fs.Int("port", 19443, "service port") startupDelay := fs.Duration("startup-delay", 30*time.Second, "time before /readyz returns 200") aggregatorURL := fs.String("aggregator", "", "aggregator URL for pushing events") + useTLS := fs.Bool("tls", false, "serve traffic and /readyz over TLS") + tlsCert := fs.String("tls-cert", "", "path to TLS certificate PEM (required with --tls)") + tlsKey := fs.String("tls-key", "", "path to TLS private key PEM (required with --tls)") fs.Parse(args) + if *useTLS && (*tlsCert == "" || *tlsKey == "") { + fmt.Fprintf(os.Stderr, "serve: --tls requires --tls-cert and --tls-key\n") + os.Exit(1) + } + // Server identity: POD_NAME env var, fallback to hostname. serverID := os.Getenv("POD_NAME") if serverID == "" { @@ -363,6 +371,13 @@ func runServe(args []string) { } }() + if *useTLS { + log.Printf("[serve] TLS enabled (cert=%s key=%s)", *tlsCert, *tlsKey) + if err := http.ListenAndServeTLS(addr, *tlsCert, *tlsKey, nil); err != nil { + log.Fatalf("[serve] ListenAndServeTLS failed: %v", err) + } + return + } if err := http.ListenAndServe(addr, nil); err != nil { log.Fatalf("[serve] ListenAndServe failed: %v", err) } diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md b/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md index e1c951475..41914ceaf 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md +++ b/openshift-tests/ccm-aws-tests/e2e/aws/health/TEST_CASES.md @@ -332,6 +332,39 @@ $BIN run-test "...multi-client KAS-config preserve_client_ip=true..." --- +## Scenario 5.5-SDK-multi-kas-tls — Multi-Client, KAS TG Config + TLS + +**Report label:** `5.5-SDK-multi-kas-tls (Multi-Client + KAS TG Config + TLS / OCPBUGS-86789)` + +**Ginkgo:** `SDK-managed NLB pre-readyz routing, multi-client KAS-config TLS (OCPBUGS-86789)` + +Clone of **5.5-SDK-multi-kas** with TLS end-to-end on the same port (`19443`): +- healthserver serves traffic and `/readyz` via `ListenAndServeTLS` +- NLB health check uses **HTTPS** `/readyz` on port `19443` +- client DaemonSet uses `https://NLB:19443/` with `--tls-insecure` +- aggregator and client metrics remain plain HTTP (unchanged) + +| Parameter | Value | +|-----------|-------| +| Client | DaemonSet on workers, 16 workers × 50ms per pod | +| Traffic | **TLS** (self-signed cert, wildcard SAN `*`) | +| HC protocol | **HTTPS** `/readyz` on same port | +| preserve_client_ip | false | +| connection_termination | false | +| draining_interval | 300s | + +**Purpose:** Determine whether OCPBUGS-86789 pre-readyz routing is specific to the +TLS handshake path (NLB may route after TCP+TLS up but before `/readyz` returns 200). + +**Run:** +```bash +$BIN run-test "...multi-client KAS-config TLS..." +``` + +**Code:** `lb_health_transition.go`, `tls_certs.go`, `buildHealthserverDaemonSetTLS()`, `serve.go` `--tls` + +--- + ## SDK Variants — Comparison Matrix All SDK variants share: healthserver DaemonSet on masters, SDK-managed NLB, same @@ -347,12 +380,13 @@ HC config (HTTP `/readyz`, interval=10s, threshold=2), same rollout simulation | 5.5-SDK-multi | DS/worker, 16w | true | true (default) | 0 (default) | Clients yes, TG no | | 5.5-SDK-multi-no-cip | DS/worker, 16w | false | true (default) | 0 (default) | Clients yes, TG no | -### Real KAS TG attributes (5.5-SDK-multi-kas, 5.5-SDK-multi-kas-cip) +### Real KAS TG attributes (5.5-SDK-multi-kas, 5.5-SDK-multi-kas-cip, 5.5-SDK-multi-kas-tls) -| Scenario | Client | preserve_client_ip | conn_term | draining | KAS-faithful | -|----------|--------|-------------------|-----------|----------|--------------| -| 5.5-SDK-multi-kas | DS/worker, 16w | false | **false** | **300s** | **Yes (most faithful)** | -| 5.5-SDK-multi-kas-cip | DS/worker, 16w | true | **false** | **300s** | CIP comparison | +| Scenario | Client | preserve_client_ip | conn_term | draining | TLS/HC | KAS-faithful | +|----------|--------|-------------------|-----------|----------|--------|--------------| +| 5.5-SDK-multi-kas | DS/worker, 16w | false | **false** | **300s** | HTTP/HTTP | **Yes (most faithful HTTP)** | +| 5.5-SDK-multi-kas-cip | DS/worker, 16w | true | **false** | **300s** | HTTP/HTTP | CIP comparison | +| 5.5-SDK-multi-kas-tls | DS/worker, 16w | false | **false** | **300s** | **TLS/HTTPS** | **Yes (TLS + HC)** | **Real KAS TG attributes** (from `aws elbv2 describe-target-group-attributes`): ``` @@ -431,18 +465,19 @@ pre-readyz routing is NLB-specific or general to all AWS LBs. ## Summary Table -| Scenario | LB Type | Managed by | Workload | Client | preserve_client_ip | conn_term / draining | Tests | -|----------|---------|------------|----------|--------|-------------------|---------------------|-------| -| 5.5 | NLB | Kubernetes | Deployment | 1 pod | true (svc default) | default | Pre-readyz (OCPBUGS) | -| 5.5-CAPA | NLB | Kubernetes | Deployment | 1 pod | true | default | Pre-readyz + CAPA TG | -| 5.5-SDK | NLB | AWS SDK | DaemonSet | 1 pod, 32w | true | default | KAS-equivalent baseline | -| 5.5-SDK-no-cip | NLB | AWS SDK | DaemonSet | 1 pod, 32w | **false** | default | Stickiness isolation | -| 5.5-SDK-multi | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | default | Multi-client baseline | -| 5.5-SDK-multi-no-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | default | Multi + no stickiness | -| 5.5-SDK-multi-kas | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | **false / 300s** | **Most faithful KAS repro** | -| 5.5-SDK-multi-kas-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | **false / 300s** | KAS TG + CIP comparison | -| 5.2 | NLB | Kubernetes | Deployment | 1 pod | true | default | Shutdown propagation (SPLAT-307) | -| 5.5-CLB | CLB | Kubernetes | Deployment | 1 pod | N/A | N/A | Pre-readyz CLB baseline | +| Scenario | LB Type | Managed by | Workload | Client | preserve_client_ip | conn_term / draining | TLS/HC | Tests | +|----------|---------|------------|----------|--------|-------------------|---------------------|--------|-------| +| 5.5 | NLB | Kubernetes | Deployment | 1 pod | true (svc default) | default | HTTP | Pre-readyz (OCPBUGS) | +| 5.5-CAPA | NLB | Kubernetes | Deployment | 1 pod | true | default | HTTP | Pre-readyz + CAPA TG | +| 5.5-SDK | NLB | AWS SDK | DaemonSet | 1 pod, 32w | true | default | HTTP | KAS-equivalent baseline | +| 5.5-SDK-no-cip | NLB | AWS SDK | DaemonSet | 1 pod, 32w | **false** | default | HTTP | Stickiness isolation | +| 5.5-SDK-multi | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | default | HTTP | Multi-client baseline | +| 5.5-SDK-multi-no-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | default | HTTP | Multi + no stickiness | +| 5.5-SDK-multi-kas | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | **false / 300s** | HTTP | **Most faithful KAS repro (HTTP)** | +| 5.5-SDK-multi-kas-cip | NLB | AWS SDK | DaemonSet | DS/worker, 16w | true | **false / 300s** | HTTP | KAS TG + CIP comparison | +| 5.5-SDK-multi-kas-tls | NLB | AWS SDK | DaemonSet | DS/worker, 16w | **false** | **false / 300s** | **TLS/HTTPS** | **KAS TLS + HC repro** | +| 5.2 | NLB | Kubernetes | Deployment | 1 pod | true | default | HTTP | Shutdown propagation (SPLAT-307) | +| 5.5-CLB | CLB | Kubernetes | Deployment | 1 pod | N/A | N/A | HTTP | Pre-readyz CLB baseline | **Pass criteria (all 5.5* scenarios):** - `PreReadyzReqCount == 0` — no requests before `/readyz → 200` @@ -450,6 +485,7 @@ pre-readyz routing is NLB-specific or general to all AWS LBs. **Informational (always reported, not a failure):** - Shutdown propagation delay (t5→t7, ~20–35s) — expected NLB HC lag +- `Late_conn_reqs` — requests to target after 80% of `kasShutdownDelay` (153.6s); high RST risk window ## Related Plans diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go index a8fbbc35d..955b88158 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/lb_health_transition.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" + elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/openshift/cluster-cloud-controller-manager-operator/openshift-tests/ccm-aws-tests/e2e/aws/health" @@ -96,8 +97,9 @@ type transitionTimeline struct { T10 time.Time // first client request to target after t9 // Counters - UnhealthyReqCount int // requests served by target between t5 and t7 - PreReadyzReqCount int // requests with X-Server-State: pre-readyz + UnhealthyReqCount int // requests served by target between t5 and t7 + PreReadyzReqCount int // requests with X-Server-State: pre-readyz + LateConnectionCount int // requests to target after 80% of kasShutdownDelay and before/at t7 // Identity TargetPod string @@ -1998,6 +2000,238 @@ var _ = Describe(healthTransitionTestPrefix, func() { framework.Logf("\n%s", report) }) }) + + // ── Scenario 5.5-SDK-multi-kas-tls ────────────────────────────────── + // Clone of 5.5-SDK-multi-kas with TLS on traffic port and HTTPS HC on + // the same port/path (/readyz), matching real KAS end-to-end TLS behaviour. + Context("SDK-managed NLB pre-readyz routing, multi-client KAS-config TLS (OCPBUGS-86789)", func() { + It("should not route to pre-readyz targets "+ + "with instance:port targeting, KAS TG attributes, HTTPS HC, TLS traffic, and multiple client IPs", func(ctx context.Context) { + + image := os.Getenv(envHealthserverImage) + if image == "" { + Skip(fmt.Sprintf("%s not set", envHealthserverImage)) + } + + var replicas int32 + startupDelay := 60 * time.Second + shutdownDelay := kasShutdownDelay + deployName := "healthserver" + clientDSName := "healthtest-client" + + var sdkNLB *SDKManagedNLB + var sgRuleID string + var masterSGID string + DeferCleanup(func(cleanupCtx context.Context) { + framework.Logf("cleaning up SDK-multi-kas-tls test resources") + if sdkNLB != nil { + elbC, err := createAWSClientLoadBalancer(cleanupCtx) + if err == nil { + ec2C, ec2Err := createAWSClientEC2(cleanupCtx) + if ec2Err != nil { + framework.Logf("WARNING: failed to create EC2 client for NLB cleanup: %v", ec2Err) + } + deleteSDKManagedNLB(cleanupCtx, elbC, ec2C, sdkNLB) + } + } + if sgRuleID != "" && masterSGID != "" { + ec2C, err := createAWSClientEC2(cleanupCtx) + if err == nil { + removeSGIngressRule(cleanupCtx, ec2C, masterSGID, sgRuleID) + } + } + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, deployName, metav1.DeleteOptions{}) + _ = cs.AppsV1().DaemonSets(ns.Name).Delete(cleanupCtx, clientDSName, metav1.DeleteOptions{}) + _ = cs.CoreV1().Pods(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().Services(ns.Name).Delete(cleanupCtx, "healthtest-aggregator", metav1.DeleteOptions{}) + _ = cs.CoreV1().ConfigMaps(ns.Name).Delete(cleanupCtx, healthserverTLSConfigMap, metav1.DeleteOptions{}) + }) + + By("deploying aggregator pod + service on worker node") + aggregatorURL := deployAggregator(ctx, cs, ns.Name, image) + + By("granting privileged SCC to default service account") + grantHostNetworkSCC(ctx, cs, ns.Name) + + By("creating TLS cert ConfigMap for healthserver") + err := ensureHealthserverTLSConfigMap(ctx, cs, ns.Name) + framework.ExpectNoError(err, "create TLS configmap") + + By("creating healthserver DaemonSet with TLS (scheduled on master nodes, hostNetwork)") + ds := buildHealthserverDaemonSetTLS(ns.Name, deployName, startupDelay, image, aggregatorURL) + var setupTimes transitionTimeline + setupTimes.T0 = time.Now() + _, err = cs.AppsV1().DaemonSets(ns.Name).Create(ctx, ds, metav1.CreateOptions{}) + framework.ExpectNoError(err, "create daemonset") + + By("waiting for DaemonSet rollout") + replicas, err = waitForDaemonSetReady(ctx, cs, ns.Name, deployName, 5*time.Minute) + framework.ExpectNoError(err, "daemonset rollout") + setupTimes.T1 = time.Now() + + By("discovering cluster infrastructure (VPC, subnets, master instances, SG)") + ec2Client, err := createAWSClientEC2(ctx) + framework.ExpectNoError(err, "create EC2 client") + elbClient, err := createAWSClientLoadBalancer(ctx) + framework.ExpectNoError(err, "create ELB client") + + infra, err := discoverClusterInfra(ctx, cs, ec2Client) + framework.ExpectNoError(err, "discover cluster infrastructure") + + By(fmt.Sprintf("adding SG inbound rule for TCP %d on master SG %s", healthserverPort, infra.MasterSGID)) + masterSGID = infra.MasterSGID + sgRuleID, err = addSGIngressRule(ctx, ec2Client, infra.MasterSGID, int32(healthserverPort)) + framework.ExpectNoError(err, "add SG inbound rule") + + By("creating SDK-managed NLB with instance:19443 targets and HTTPS HC") + sdkNLB, err = createSDKManagedNLB(ctx, elbClient, ec2Client, infra, int32(healthserverPort), SDKNLBCreateOpts{ + HealthCheckProtocol: elbv2types.ProtocolEnumHttps, + }) + framework.ExpectNoError(err, "create SDK-managed NLB") + sdkNLB.SGID = masterSGID + sdkNLB.SGRuleID = sgRuleID + setupTimes.T2 = time.Now() + + By("applying KAS-equivalent TG attributes (conn_term=false, draining=300s, preserve_client_ip=false)") + err = setTGKASAttributes(ctx, elbClient, sdkNLB.TGARN, false) + framework.ExpectNoError(err, "set KAS TG attributes") + + observer := health.NewObserver(elbClient, 1*time.Second) + err = observer.DiscoverTargetGroup(ctx, sdkNLB.NLBARN) + framework.ExpectNoError(err, "discover target group") + + By("waiting for ALL SDK NLB targets to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "all SDK NLB targets healthy") + setupTimes.T3 = time.Now() + + By("deploying client DaemonSet on worker nodes (TLS to NLB, one pod per worker)") + clientPodNames := deployClientDaemonSet(ctx, cs, ns.Name, image, sdkNLB.NLBDNS, aggregatorURL, + defaultClientWorkers, defaultClientInterval, true) + + svcCfg := serviceConfig{ + LBDNS: sdkNLB.NLBDNS, + LBARN: sdkNLB.NLBARN, + TGARN: observer.TargetGroupARN(), + TGTargetType: observer.TargetType(), + Platform: "AWS", + ServiceAnnotations: map[string]string{ + "sdk-managed": "true", + "client-mode": "multi-client-daemonset", + "preserve_client_ip": "false", + "connection_termination": "false", + "draining_interval": "300", + "tls": "true", + "hc-protocol": "HTTPS", + "target-port": fmt.Sprintf("%d", healthserverPort), + "traffic-port": fmt.Sprintf("%d", healthserverPort), + "hc-port": fmt.Sprintf("%d", healthserverPort), + "same-port-traffic-and-hc": "true", + }, + } + if region, rErr := common.GetRegionFromInfrastructure(ctx); rErr == nil { + svcCfg.Region = region + } + if isExternal, tErr := common.IsExternalTopology(ctx); tErr == nil { + if isExternal { + svcCfg.Topology = "External (HyperShift)" + } else { + svcCfg.Topology = "HighlyAvailable" + } + } + tgAttrs, err := observer.DescribeTGAttributes(ctx) + if err == nil { + svcCfg.TGAttributes = tgAttrs + } + fetchTGHealthCheckConfig(ctx, &svcCfg) + + observer.Start(ctx) + stopTGPush := startTGSnapshotPusher(ctx, cs, ns.Name, observer) + defer func() { stopTGPush(); observer.Stop() }() + + By(fmt.Sprintf("verifying steady state for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + steadyRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + steadyNonReady := 0 + for _, r := range steadyRecords { + if r.IsNonReadyReq { + steadyNonReady++ + } + } + Expect(steadyNonReady).To(Equal(0), "pre-readyz responses during steady state") + + By("listing pods to identify target for rollout simulation") + pods, err := cs.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app=%s", deployName), + }) + framework.ExpectNoError(err, "list healthserver pods") + Expect(len(pods.Items)).To(BeNumerically(">=", int(replicas))) + + knownServers := make(map[string]bool) + podNodeMap := make(map[string]string) + for _, p := range pods.Items { + knownServers[p.Name] = true + podNodeMap[p.Name] = p.Spec.NodeName + } + targetPod := pods.Items[0].Name + targetNode := pods.Items[0].Spec.NodeName + + By("deleting target pod (t5/t7.1 — SIGTERM triggers readyz→503)") + t5 := time.Now() + t71 := t5 + err = cs.CoreV1().Pods(ns.Name).Delete(ctx, targetPod, metav1.DeleteOptions{}) + framework.ExpectNoError(err) + + By("waiting for replacement pod on same node (DaemonSet guarantee)") + newPod := waitForNewPodFromSet(ctx, cs, ns.Name, deployName, knownServers) + newPodObj, npErr := cs.CoreV1().Pods(ns.Name).Get(ctx, newPod, metav1.GetOptions{}) + if npErr == nil { + podNodeMap[newPod] = newPodObj.Spec.NodeName + if newPodObj.Spec.NodeName != targetNode { + framework.Logf("WARNING: [daemonset] replacement pod %s landed on %s, expected %s", + newPod, newPodObj.Spec.NodeName, targetNode) + } else { + framework.Logf("[daemonset] replacement pod %s on same node %s (verified)", newPod, targetNode) + } + } + + By("waiting for TG to detect unhealthy target") + waitForTGUnhealthy(ctx, observer, 3*time.Minute) + + By("waiting for restarted target to become healthy") + err = waitForAllTGTargetsHealthy(ctx, observer, 10*time.Minute) + framework.ExpectNoError(err, "restarted target healthy") + + By(fmt.Sprintf("observing post-recovery traffic for %s", postHealthyObserve)) + time.Sleep(postHealthyObserve) + + allRecords := fetchMergedClientRecords(ctx, cs, ns.Name, clientPodNames) + allEvents := observer.Events() + + tl := computeTimeline(targetPod, knownServers, t5, t71, allRecords, allEvents) + tl.T0 = setupTimes.T0 + tl.T1 = setupTimes.T1 + tl.T2 = setupTimes.T2 + tl.T3 = setupTimes.T3 + for _, r := range steadyRecords { + if r.Error == "" && r.HTTPStatus > 0 { + tl.T4 = r.Timestamp + break + } + } + tl.TargetPod = targetPod + tl.TargetNode = targetNode + tl.NewPod = newPod + tl.PodNodeMap = podNodeMap + + report := buildReport("5.5-SDK-multi-kas-tls (Multi-Client + KAS TG Config + TLS / OCPBUGS-86789)", + tl, svcCfg, replicas, startupDelay, shutdownDelay, + allRecords, allEvents, observer.Snapshots()) + report += buildVerdict55(tl, allRecords) + framework.Logf("\n%s", report) + }) + }) }) // ─── Setup helper ─────────────────────────────────────────────────────────── @@ -2359,6 +2593,7 @@ func computeTimeline( // t7: last request served by the OLD target pod after t5. // Each request after readyz→503 counts as an "unhealthy" request. + lateThreshold := t5.Add(time.Duration(float64(kasShutdownDelay) * 0.8)) for _, r := range records { if r.Timestamp.Before(t5) { continue @@ -2366,6 +2601,9 @@ func computeTimeline( if r.ServerID == oldPod { tl.T7 = r.Timestamp tl.UnhealthyReqCount++ + if r.Timestamp.After(lateThreshold) { + tl.LateConnectionCount++ + } } } @@ -2607,6 +2845,7 @@ func buildReport( w("%-25s %-14s %-14s %s", "T_tg_unhealthy", fmtDur(tl.T5, tl.T6), "~20s", "t6-t5: HC detect unhealthy") w("%-25s %-14s %-14s %s", "T_route_stop", fmtDur(tl.T5, tl.T7), " 0 { + w(" [LATE-CONN] %d request(s) routed after 80%% of shutdown delay (pod kill imminent)", + tl.LateConnectionCount) + } + return b.String() } @@ -3172,6 +3416,32 @@ func buildHealthserverDaemonSet(namespace, name string, startupDelay time.Durati } } +// buildHealthserverDaemonSetTLS is like buildHealthserverDaemonSet but serves +// traffic and /readyz over TLS using a mounted self-signed cert (KAS-like). +func buildHealthserverDaemonSetTLS(namespace, name string, startupDelay time.Duration, image, aggregatorURL string) *appsv1.DaemonSet { + ds := buildHealthserverDaemonSet(namespace, name, startupDelay, image, aggregatorURL) + c := &ds.Spec.Template.Spec.Containers[0] + c.Args = append(c.Args, + "--tls", + fmt.Sprintf("--tls-cert=%s/%s", healthserverTLSMountPath, healthserverTLSCertFile), + fmt.Sprintf("--tls-key=%s/%s", healthserverTLSMountPath, healthserverTLSKeyFile), + ) + c.VolumeMounts = append(c.VolumeMounts, v1.VolumeMount{ + Name: "healthserver-tls", + MountPath: healthserverTLSMountPath, + ReadOnly: true, + }) + ds.Spec.Template.Spec.Volumes = append(ds.Spec.Template.Spec.Volumes, v1.Volume{ + Name: "healthserver-tls", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{Name: healthserverTLSConfigMap}, + }, + }, + }) + return ds +} + // waitForDaemonSetReady polls the DaemonSet status until NumberReady equals // DesiredNumberScheduled (and DesiredNumberScheduled > 0), or the timeout // is reached. Returns the DesiredNumberScheduled count. @@ -3427,10 +3697,27 @@ func deployInClusterClient(ctx context.Context, cs clientset.Interface, namespac // node. Because each pod has a different source IP, the NLB distributes // traffic across all targets even with preserve_client_ip.enabled=true. // Returns the list of pod names created by the DaemonSet. -func deployClientDaemonSet(ctx context.Context, cs clientset.Interface, namespace, image, nlbDNS, aggregatorURL string, workers int, interval time.Duration) []string { +// Pass useTLS=true to use https:// against the NLB with --tls-insecure. +func deployClientDaemonSet(ctx context.Context, cs clientset.Interface, namespace, image, nlbDNS, aggregatorURL string, workers int, interval time.Duration, useTLS ...bool) []string { dsName := "healthtest-client" labels := map[string]string{"app": dsName} + scheme := "http" + if len(useTLS) > 0 && useTLS[0] { + scheme = "https" + } + clientArgs := []string{ + "client", + fmt.Sprintf("--url=%s://%s:%d/", scheme, nlbDNS, healthserverPort), + fmt.Sprintf("--workers=%d", workers), + fmt.Sprintf("--interval=%s", interval), + fmt.Sprintf("--port=%d", clientPort), + fmt.Sprintf("--aggregator=%s", aggregatorURL), + } + if scheme == "https" { + clientArgs = append(clientArgs, "--tls-insecure") + } + ds := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{Name: dsName, Namespace: namespace}, Spec: appsv1.DaemonSetSpec{ @@ -3449,14 +3736,7 @@ func deployClientDaemonSet(ctx context.Context, cs clientset.Interface, namespac FieldRef: &v1.ObjectFieldSelector{FieldPath: "status.podIP"}, }, }}, - Args: []string{ - "client", - fmt.Sprintf("--url=http://%s:%d/", nlbDNS, healthserverPort), - fmt.Sprintf("--workers=%d", workers), - fmt.Sprintf("--interval=%s", interval), - fmt.Sprintf("--port=%d", clientPort), - fmt.Sprintf("--aggregator=%s", aggregatorURL), - }, + Args: clientArgs, Ports: []v1.ContainerPort{{ Name: "http", ContainerPort: int32(clientPort), diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go b/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go index 837f57b19..5e9aec72c 100644 --- a/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go +++ b/openshift-tests/ccm-aws-tests/e2e/aws/sdk_nlb.go @@ -45,6 +45,12 @@ type ClusterInfra struct { MasterSGID string } +// SDKNLBCreateOpts configures optional SDK NLB creation parameters. +// Zero values use defaults matching existing HTTP-based tests. +type SDKNLBCreateOpts struct { + HealthCheckProtocol elbv2types.ProtocolEnum // default HTTP +} + // discoverClusterInfra discovers VPC, subnets, master instance IDs, and master // security group from the running cluster. Instance IDs come from K8s node // spec.providerID (reliable, no EC2 tag assumptions). VPC, subnets, and SG @@ -242,7 +248,11 @@ func removeSGIngressRule(ctx context.Context, ec2Client *ec2.Client, sgID, ruleI // createSDKManagedNLB creates an NLB, target group, and listener via the AWS // SDK, replicating how the OCP installer provisions the KAS NLB. -func createSDKManagedNLB(ctx context.Context, elbClient *elbv2.Client, ec2Client *ec2.Client, infra *ClusterInfra, port int32) (*SDKManagedNLB, error) { +func createSDKManagedNLB(ctx context.Context, elbClient *elbv2.Client, ec2Client *ec2.Client, infra *ClusterInfra, port int32, opts ...SDKNLBCreateOpts) (*SDKManagedNLB, error) { + hcProtocol := elbv2types.ProtocolEnumHttp + if len(opts) > 0 && opts[0].HealthCheckProtocol != "" { + hcProtocol = opts[0].HealthCheckProtocol + } // Use the infra ID truncated to fit AWS 32-char name limit. // Strip trailing dashes to satisfy AWS naming regex: (?!.*-$)^[A-Za-z0-9-]+$ shortID := infra.InfraID @@ -261,7 +271,7 @@ func createSDKManagedNLB(ctx context.Context, elbClient *elbv2.Client, ec2Client } // 1. Create target group. - framework.Logf("creating target group %s (port %d)", resourceName, port) + framework.Logf("creating target group %s (port %d, hc=%s)", resourceName, port, hcProtocol) tgInput := &elbv2.CreateTargetGroupInput{ Name: awssdk.String(resourceName), TargetType: elbv2types.TargetTypeEnumInstance, @@ -269,7 +279,7 @@ func createSDKManagedNLB(ctx context.Context, elbClient *elbv2.Client, ec2Client Port: awssdk.Int32(port), VpcId: awssdk.String(infra.VPCID), HealthCheckEnabled: awssdk.Bool(true), - HealthCheckProtocol: elbv2types.ProtocolEnumHttp, + HealthCheckProtocol: hcProtocol, HealthCheckPath: awssdk.String("/readyz"), HealthCheckPort: awssdk.String(fmt.Sprintf("%d", port)), HealthCheckIntervalSeconds: awssdk.Int32(10), diff --git a/openshift-tests/ccm-aws-tests/e2e/aws/tls_certs.go b/openshift-tests/ccm-aws-tests/e2e/aws/tls_certs.go new file mode 100644 index 000000000..55628531a --- /dev/null +++ b/openshift-tests/ccm-aws-tests/e2e/aws/tls_certs.go @@ -0,0 +1,101 @@ +package aws + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" +) + +const ( + healthserverTLSConfigMap = "healthserver-tls" + healthserverTLSCertFile = "tls.crt" + healthserverTLSKeyFile = "tls.key" + healthserverTLSMountPath = "/etc/healthserver-tls" +) + +// generateSelfSignedTLSCert creates a self-signed cert/key pair for test use. +// The cert includes a wildcard DNS SAN so NLB HC and clients are not blocked. +func generateSelfSignedTLSCert() (certPEM, keyPEM []byte, err error) { + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, fmt.Errorf("generate key: %w", err) + } + + serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + return nil, nil, fmt.Errorf("generate serial: %w", err) + } + + template := &x509.Certificate{ + SerialNumber: serial, + Subject: pkix.Name{CommonName: "healthserver-e2e"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + DNSNames: []string{"*"}, + } + + der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + return nil, nil, fmt.Errorf("create certificate: %w", err) + } + + certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) + keyBytes, err := x509.MarshalECPrivateKey(key) + if err != nil { + return nil, nil, fmt.Errorf("marshal key: %w", err) + } + keyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}) + return certPEM, keyPEM, nil +} + +// ensureHealthserverTLSConfigMap creates or updates a ConfigMap with a +// self-signed TLS cert/key for the healthserver DaemonSet. +func ensureHealthserverTLSConfigMap(ctx context.Context, cs clientset.Interface, namespace string) error { + certPEM, keyPEM, err := generateSelfSignedTLSCert() + if err != nil { + return err + } + + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: healthserverTLSConfigMap, + Namespace: namespace, + }, + Data: map[string]string{ + healthserverTLSCertFile: string(certPEM), + healthserverTLSKeyFile: string(keyPEM), + }, + } + + existing, getErr := cs.CoreV1().ConfigMaps(namespace).Get(ctx, healthserverTLSConfigMap, metav1.GetOptions{}) + if getErr != nil { + _, err = cs.CoreV1().ConfigMaps(namespace).Create(ctx, cm, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("create TLS configmap: %w", err) + } + framework.Logf("created TLS configmap %s/%s", namespace, healthserverTLSConfigMap) + return nil + } + + existing.Data = cm.Data + _, err = cs.CoreV1().ConfigMaps(namespace).Update(ctx, existing, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("update TLS configmap: %w", err) + } + framework.Logf("updated TLS configmap %s/%s", namespace, healthserverTLSConfigMap) + return nil +}