From bccb95d8477db2b23dd9cb20e88d62890048ad2e Mon Sep 17 00:00:00 2001 From: Praveen Jaya Kumar Date: Wed, 5 Nov 2025 21:13:25 +0530 Subject: [PATCH 01/10] MG-128: Add/Update e2e tests --- Makefile | 19 + README.md | 27 +- examples/mustgather_basic.yaml | 9 +- examples/mustgather_full.yaml | 11 +- examples/mustgather_non_internal_user.yaml | 11 +- examples/mustgather_proxy.yaml | 9 +- examples/mustgather_retain_resources.yaml | 11 +- examples/mustgather_timeout.yaml | 9 +- go.mod | 2 +- test/e2e/must_gather_operator_runner_test.go | 4 +- test/e2e/must_gather_operator_tests.go | 888 ++++++++++++++++++- test/must-gather.yaml | 9 +- 12 files changed, 944 insertions(+), 65 deletions(-) diff --git a/Makefile b/Makefile index 9c5105d72..52933ddc7 100644 --- a/Makefile +++ b/Makefile @@ -5,3 +5,22 @@ include boilerplate/generated-includes.mk .PHONY: boilerplate-update boilerplate-update: @boilerplate/update + + +# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. +E2E_TIMEOUT ?= 1h +# E2E_GINKGO_LABEL_FILTER is ginkgo label query for selecting tests. See +# https://onsi.github.io/ginkgo/#spec-labels. The default is to run tests on the AWS platform. +E2E_GINKGO_LABEL_FILTER ?= "Platform: isSubsetOf {AWS}" +.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. +test-e2e: + go test \ + -timeout $(E2E_TIMEOUT) \ + -count 1 \ + -v \ + -p 1 \ + -tags e2e \ + ./test/e2e \ + -ginkgo.v \ + -ginkgo.show-node-events \ + -ginkgo.label-filter=$(E2E_GINKGO_LABEL_FILTER) \ No newline at end of file diff --git a/README.md b/README.md index 719e69107..1e4ff0250 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,14 @@ kind: MustGather metadata: name: example-mustgather-basic spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds ``` This request will collect the standard must-gather info and upload it to case `#02527285` using the credentials found in the `caseManagementCreds` secret. @@ -27,11 +30,14 @@ kind: MustGather metadata: name: example-mustgather-full spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds audit: true ``` @@ -45,11 +51,14 @@ kind: MustGather metadata: name: example-mustgather-proxy spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds proxyConfig: httpProxy: http://myproxy httpsProxy: https://my_http_proxy diff --git a/examples/mustgather_basic.yaml b/examples/mustgather_basic.yaml index b84d46e51..1bca820fd 100644 --- a/examples/mustgather_basic.yaml +++ b/examples/mustgather_basic.yaml @@ -3,8 +3,11 @@ kind: MustGather metadata: name: example-mustgather spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds diff --git a/examples/mustgather_full.yaml b/examples/mustgather_full.yaml index 2c9af4944..a1363eb87 100644 --- a/examples/mustgather_full.yaml +++ b/examples/mustgather_full.yaml @@ -3,8 +3,11 @@ kind: MustGather metadata: name: full-mustgather spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: - name: must-gather-admin \ No newline at end of file + name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds \ No newline at end of file diff --git a/examples/mustgather_non_internal_user.yaml b/examples/mustgather_non_internal_user.yaml index 3c1f7b1eb..4ddcab262 100644 --- a/examples/mustgather_non_internal_user.yaml +++ b/examples/mustgather_non_internal_user.yaml @@ -3,9 +3,12 @@ kind: MustGather metadata: name: example-mustgather spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin - internalUser: false + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds + internalUser: false diff --git a/examples/mustgather_proxy.yaml b/examples/mustgather_proxy.yaml index 5293986e2..5252f433b 100644 --- a/examples/mustgather_proxy.yaml +++ b/examples/mustgather_proxy.yaml @@ -3,11 +3,14 @@ kind: MustGather metadata: name: example-mustgather-proxy spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds proxyConfig: httpProxy: http://myproxy httpsProxy: https://my_http_proxy diff --git a/examples/mustgather_retain_resources.yaml b/examples/mustgather_retain_resources.yaml index 0db65a682..c38825576 100644 --- a/examples/mustgather_retain_resources.yaml +++ b/examples/mustgather_retain_resources.yaml @@ -1,11 +1,14 @@ -apiVersion: managed.openshift.io/v1alpha1 +apiVersion: operator.openshift.io/v1alpha1 kind: MustGather metadata: name: example-mustgather-retain-resources spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds retainResourcesOnCompletion: true diff --git a/examples/mustgather_timeout.yaml b/examples/mustgather_timeout.yaml index 58450efac..a3e0136f4 100644 --- a/examples/mustgather_timeout.yaml +++ b/examples/mustgather_timeout.yaml @@ -3,9 +3,12 @@ kind: MustGather metadata: name: example-mustgather spec: - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds mustGatherTimeout: "2m" diff --git a/go.mod b/go.mod index e9023f774..dae2733d6 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/prometheus/client_golang v1.22.0 github.com/redhat-cop/operator-utils v1.3.7 k8s.io/api v0.33.3 + k8s.io/apiextensions-apiserver v0.33.0 k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.3 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff @@ -88,7 +89,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.33.0 // indirect k8s.io/cli-runtime v0.28.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kubectl v0.28.2 // indirect diff --git a/test/e2e/must_gather_operator_runner_test.go b/test/e2e/must_gather_operator_runner_test.go index 3138ddeb4..9b4fcd3a9 100644 --- a/test/e2e/must_gather_operator_runner_test.go +++ b/test/e2e/must_gather_operator_runner_test.go @@ -1,7 +1,7 @@ // THIS FILE IS GENERATED BY BOILERPLATE. DO NOT EDIT. -//go:build osde2e +//go:build e2e -package osde2etests +package e2e import ( "os" diff --git a/test/e2e/must_gather_operator_tests.go b/test/e2e/must_gather_operator_tests.go index 439c41d41..fac3821f9 100644 --- a/test/e2e/must_gather_operator_tests.go +++ b/test/e2e/must_gather_operator_tests.go @@ -1,31 +1,39 @@ -// DO NOT REMOVE TAGS BELOW. IF ANY NEW TEST FILES ARE CREATED UNDER /osde2e, PLEASE ADD THESE TAGS TO THEM IN ORDER TO BE EXCLUDED FROM UNIT TESTS. -//go:build osde2e -// +build osde2e +// DO NOT REMOVE TAGS BELOW. IF ANY NEW TEST FILES ARE CREATED UNDER /test/e2e, PLEASE ADD THESE TAGS TO THEM IN ORDER TO BE EXCLUDED FROM UNIT TESTS. +//go:build e2e +// +build e2e -package osde2etests +package e2e import ( "context" + "fmt" + "strings" + "time" "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" mustgatherv1alpha1 "github.com/openshift/must-gather-operator/api/v1alpha1" "github.com/openshift/osde2e-common/pkg/clients/openshift" + . "github.com/openshift/osde2e-common/pkg/gomega/assertions" . "github.com/openshift/osde2e-common/pkg/gomega/matchers" + authorizationv1 "k8s.io/api/authorization/v1" + batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/log" ) var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { var ( - oc *openshift.Client - operator = "must-gather-operator" - namespace = "openshift-" + operator - service = operator - rolePrefix = operator + oc *openshift.Client + operator = "must-gather-operator" + namespace = operator + service = operator + // rolePrefix = operator clusterRolePrefix = operator ) @@ -36,6 +44,7 @@ var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { oc, err = openshift.New(ginkgo.GinkgoLogr) Expect(err).ShouldNot(HaveOccurred(), "unable to setup openshift client") Expect(mustgatherv1alpha1.AddToScheme(oc.GetScheme())).ShouldNot(HaveOccurred(), "failed to register mustgatherv1alpha1 scheme") + Expect(apiextensionsv1.AddToScheme(oc.GetScheme())).ShouldNot(HaveOccurred(), "failed to register apiextensionsv1 scheme") }) ginkgo.It("is installed", func(ctx context.Context) { @@ -43,17 +52,25 @@ var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { err := oc.Get(ctx, namespace, "", &corev1.Namespace{}) Expect(err).ShouldNot(HaveOccurred(), "namespace %s not found", namespace) - ginkgo.By("checking the role exists") - var roles rbacv1.RoleList - err = oc.WithNamespace(namespace).List(ctx, &roles) - Expect(err).ShouldNot(HaveOccurred(), "failed to list roles") - Expect(&roles).Should(ContainItemWithPrefix(rolePrefix), "unable to find roles with prefix %s", rolePrefix) + ginkgo.By("checking the operator service account exists") + err = oc.Get(ctx, operator, namespace, &corev1.ServiceAccount{}) + Expect(err).ShouldNot(HaveOccurred(), "service account %s/%s not found", namespace, operator) + + ginkgo.By("checking the must-gather-admin service account exists") + err = oc.Get(ctx, "must-gather-admin", namespace, &corev1.ServiceAccount{}) + Expect(err).ShouldNot(HaveOccurred(), "service account %s/must-gather-admin not found", namespace) + + // ginkgo.By("checking the role exists") + // var roles rbacv1.RoleList + // err = oc.WithNamespace(namespace).List(ctx, &roles) + // Expect(err).ShouldNot(HaveOccurred(), "failed to list roles") + // Expect(&roles).Should(ContainItemWithPrefix(rolePrefix), "unable to find roles with prefix %s", rolePrefix) - ginkgo.By("checking the rolebinding exists") - var rolebindings rbacv1.RoleBindingList - err = oc.List(ctx, &rolebindings) - Expect(err).ShouldNot(HaveOccurred(), "failed to list rolebindings") - Expect(&rolebindings).Should(ContainItemWithPrefix(rolePrefix), "unable to find rolebindings with prefix %s", rolePrefix) + // ginkgo.By("checking the rolebinding exists") + // var rolebindings rbacv1.RoleBindingList + // err = oc.List(ctx, &rolebindings) + // Expect(err).ShouldNot(HaveOccurred(), "failed to list rolebindings") + // Expect(&rolebindings).Should(ContainItemWithPrefix(rolePrefix), "unable to find rolebindings with prefix %s", rolePrefix) ginkgo.By("checking the clusterrole exists") var clusterRoles rbacv1.ClusterRoleList @@ -74,7 +91,795 @@ var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { EventuallyDeployment(ctx, oc, operator, namespace).Should(BeAvailable()) }) - ginkgo.DescribeTable("MustGather can be created", func(ctx context.Context, user, group string) { + ginkgo.It("has required service accounts with proper permissions", func(ctx context.Context) { + ginkgo.By("verifying must-gather-operator service account exists") + operatorSA := &corev1.ServiceAccount{} + err := oc.Get(ctx, operator, namespace, operatorSA) + Expect(err).ShouldNot(HaveOccurred(), "operator service account not found") + + ginkgo.By("verifying must-gather-admin service account exists") + adminSA := &corev1.ServiceAccount{} + err = oc.Get(ctx, "must-gather-admin", namespace, adminSA) + Expect(err).ShouldNot(HaveOccurred(), "must-gather-admin service account not found") + + ginkgo.By("verifying must-gather-operator clusterrole exists") + operatorCR := &rbacv1.ClusterRole{} + err = oc.Get(ctx, operator, "", operatorCR) + Expect(err).ShouldNot(HaveOccurred(), "operator clusterrole not found") + Expect(len(operatorCR.Rules)).Should(BeNumerically(">", 0), "operator clusterrole has no rules") + + ginkgo.By("verifying must-gather-operator has permissions for jobs and secrets") + hasJobPermissions := false + hasSecretPermissions := false + for _, rule := range operatorCR.Rules { + for _, apiGroup := range rule.APIGroups { + if apiGroup == "batch" { + for _, resource := range rule.Resources { + if resource == "jobs" { + hasJobPermissions = true + } + } + } + if apiGroup == "" { // core API group + for _, resource := range rule.Resources { + if resource == "secrets" { + hasSecretPermissions = true + } + } + } + } + } + Expect(hasJobPermissions).To(BeTrue(), "operator clusterrole missing job permissions") + Expect(hasSecretPermissions).To(BeTrue(), "operator clusterrole missing secret permissions") + + ginkgo.By("verifying must-gather-admin clusterrole exists") + adminCR := &rbacv1.ClusterRole{} + err = oc.Get(ctx, "must-gather-admin", "", adminCR) + Expect(err).ShouldNot(HaveOccurred(), "must-gather-admin clusterrole not found") + Expect(len(adminCR.Rules)).Should(BeNumerically(">", 0), "must-gather-admin clusterrole has no rules") + + ginkgo.By("verifying must-gather-operator clusterrolebinding exists") + operatorCRB := &rbacv1.ClusterRoleBinding{} + err = oc.Get(ctx, operator, "", operatorCRB) + Expect(err).ShouldNot(HaveOccurred(), "operator clusterrolebinding not found") + Expect(operatorCRB.RoleRef.Name).To(Equal(operator), "clusterrolebinding references wrong role") + Expect(len(operatorCRB.Subjects)).Should(BeNumerically(">", 0), "clusterrolebinding has no subjects") + + ginkgo.By("verifying must-gather-admin clusterrolebinding exists") + adminCRB := &rbacv1.ClusterRoleBinding{} + err = oc.Get(ctx, "must-gather-admin", "", adminCRB) + Expect(err).ShouldNot(HaveOccurred(), "must-gather-admin clusterrolebinding not found") + Expect(adminCRB.RoleRef.Name).To(Equal("must-gather-admin"), "admin clusterrolebinding references wrong role") + Expect(len(adminCRB.Subjects)).Should(BeNumerically(">", 0), "admin clusterrolebinding has no subjects") + + ginkgo.By("verifying clusterrolebinding references correct service account") + foundAdminSA := false + for _, subject := range adminCRB.Subjects { + if subject.Kind == "ServiceAccount" && subject.Name == "must-gather-admin" && subject.Namespace == namespace { + foundAdminSA = true + break + } + } + Expect(foundAdminSA).To(BeTrue(), "must-gather-admin clusterrolebinding does not reference must-gather-admin service account") + }) + + ginkgo.It("creates a Job with correct service account when MustGather is created", func(ctx context.Context) { + mustGatherName := "test-mustgather-sa" + + ginkgo.By("creating a MustGather CR with must-gather-admin service account") + mustgather := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: namespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "must-gather-admin", + // Not specifying UploadTarget to avoid requiring secrets + }, + } + + err := oc.Create(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather") + + ginkgo.By("verifying a Job is created with the same name") + job := &batchv1.Job{} + Eventually(func() error { + return oc.Get(ctx, mustGatherName, namespace, job) + }).WithTimeout(30*time.Second).WithPolling(2*time.Second).Should(Succeed(), "Job should be created") + + ginkgo.By("verifying the Job uses the correct service account") + Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), "Job should use must-gather-admin service account") + + ginkgo.By("verifying the Job has the gather container") + Expect(len(job.Spec.Template.Spec.Containers)).Should(BeNumerically(">=", 1), "Job should have at least one container") + hasGatherContainer := false + for _, container := range job.Spec.Template.Spec.Containers { + if container.Name == "gather" { + hasGatherContainer = true + break + } + } + Expect(hasGatherContainer).To(BeTrue(), "Job should have a gather container") + + ginkgo.By("verifying the Job has required volumes") + hasOutputVolume := false + for _, volume := range job.Spec.Template.Spec.Volumes { + if volume.Name == "must-gather-output" { + hasOutputVolume = true + break + } + } + Expect(hasOutputVolume).To(BeTrue(), "Job should have must-gather-output volume") + + ginkgo.By("cleaning up the MustGather CR") + err = oc.Delete(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather") + + ginkgo.By("verifying the Job is eventually cleaned up") + Eventually(func() bool { + err := oc.Get(ctx, mustGatherName, namespace, job) + return errors.IsNotFound(err) + }).WithTimeout(30*time.Second).WithPolling(2*time.Second).Should(BeTrue(), "Job should be cleaned up") + }) + + ginkgo.It("fails gracefully when using non-existent service account", func(ctx context.Context) { + mustGatherName := "test-mustgather-invalid-sa" + + ginkgo.By("creating a MustGather CR with non-existent service account") + mustgather := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: namespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "non-existent-service-account", + }, + } + + err := oc.Create(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "mustgather CR should be created even with invalid service account") + + ginkgo.By("verifying a Job is created") + job := &batchv1.Job{} + Eventually(func() error { + return oc.Get(ctx, mustGatherName, namespace, job) + }).WithTimeout(30*time.Second).WithPolling(2*time.Second).Should(Succeed(), "Job should be created") + + ginkgo.By("verifying the Job references the non-existent service account") + Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("non-existent-service-account")) + + ginkgo.By("verifying the Job fails to create pods due to missing service account") + // The Job should exist but pods will fail to be created + Consistently(func() int32 { + _ = oc.Get(ctx, mustGatherName, namespace, job) + return job.Status.Active + }).WithTimeout(20*time.Second).WithPolling(2*time.Second).Should(Equal(int32(0)), "Job should not have active pods due to missing service account") + + ginkgo.By("cleaning up the MustGather CR") + err = oc.Delete(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather") + }) + + ginkgo.It("successfully collects data and completes MustGather lifecycle", func(ctx context.Context) { + mustGatherName := "test-mustgather-complete" + + ginkgo.By("cleaning up any existing MustGather from previous test runs") + existingMG := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: namespace, + }, + } + _ = oc.Delete(ctx, existingMG) // Ignore error if doesn't exist + + ginkgo.By("creating a MustGather CR for data collection") + mustgather := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: namespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "must-gather-admin", + // No upload target to simplify the test - just collect data + }, + } + + err := oc.Create(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather CR") + + ginkgo.By("verifying the MustGather CR is created and has initial status") + createdMG := &mustgatherv1alpha1.MustGather{} + err = oc.Get(ctx, mustGatherName, namespace, createdMG) + Expect(err).ShouldNot(HaveOccurred(), "failed to get mustgather CR") + Expect(createdMG.Status.Completed).To(BeFalse(), "MustGather should not be completed initially") + + ginkgo.By("verifying a Job is created for must-gather collection") + job := &batchv1.Job{} + Eventually(func() error { + return oc.Get(ctx, mustGatherName, namespace, job) + }).WithTimeout(60*time.Second).WithPolling(2*time.Second).Should(Succeed(), "Job should be created by operator") + + ginkgo.By("verifying the Job is using the correct service account") + Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), "Job must use must-gather-admin service account") + + ginkgo.By("verifying pods are created for the Job") + Eventually(func() int32 { + err := oc.Get(ctx, mustGatherName, namespace, job) + if err != nil { + return 0 + } + return job.Status.Active + job.Status.Succeeded + job.Status.Failed + }).WithTimeout(60*time.Second).WithPolling(3*time.Second).Should(BeNumerically(">", 0), "Job should create at least one pod") + + ginkgo.By("waiting for the Job to start running or complete") + Eventually(func() bool { + err := oc.Get(ctx, mustGatherName, namespace, job) + if err != nil { + return false + } + // Job is running if it has active pods, or it's done if succeeded/failed + return job.Status.Active > 0 || job.Status.Succeeded > 0 || job.Status.Failed > 0 + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), "Job should start running or complete") + + ginkgo.By("verifying MustGather status is updated by the operator") + Eventually(func() bool { + err := oc.Get(ctx, mustGatherName, namespace, createdMG) + if err != nil { + return false + } + // Check if status has been updated (Status field should be set) + return createdMG.Status.Status != "" + }).WithTimeout(10*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), "MustGather status should be updated") + + ginkgo.By("checking final MustGather status") + err = oc.Get(ctx, mustGatherName, namespace, createdMG) + Expect(err).ShouldNot(HaveOccurred(), "failed to get mustgather status") + + // Log the status for debugging + ginkgo.GinkgoWriter.Printf("MustGather Status: %s\n", createdMG.Status.Status) + ginkgo.GinkgoWriter.Printf("MustGather Completed: %v\n", createdMG.Status.Completed) + ginkgo.GinkgoWriter.Printf("MustGather Reason: %s\n", createdMG.Status.Reason) + + // Verify status is either Completed or Failed (both are valid outcomes) + Expect(createdMG.Status.Status).Should(Or(Equal("Completed"), Equal("Failed"), Equal("")), + "MustGather status should be set") + + // If completed, verify the completed flag + if createdMG.Status.Status == "Completed" { + Expect(createdMG.Status.Completed).To(BeTrue(), "Completed flag should be true when status is Completed") + Expect(createdMG.Status.Reason).ToNot(BeEmpty(), "Reason should be populated") + } + + ginkgo.By("cleaning up the MustGather CR") + err = oc.Delete(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather") + }) + + ginkgo.It("successfully collects cluster resources", func(ctx context.Context) { + ginkgo.By("verifying must-gather-admin service account has required permissions") + + // Helper function to check SA permissions + checkSAPermission := func(verb, group, resource string) { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: fmt.Sprintf("system:serviceaccount:%s:must-gather-admin", namespace), + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: group, + Resource: resource, + }, + }, + } + err := oc.Create(ctx, sar) + Expect(err).ShouldNot(HaveOccurred(), "failed to check permissions for %s", resource) + + if !sar.Status.Allowed { + ginkgo.GinkgoWriter.Printf("❌ DENIED: must-gather-admin cannot %s %s (reason: %s)\n", + verb, resource, sar.Status.Reason) + } + Expect(sar.Status.Allowed).To(BeTrue(), + "must-gather-admin SA must be able to %s %s", verb, resource) + + ginkgo.GinkgoWriter.Printf("✅ must-gather-admin can %s %s\n", verb, resource) + } + + // Check all critical permissions + checkSAPermission("list", "", "pods") + checkSAPermission("get", "", "pods") + checkSAPermission("list", "apps", "deployments") + checkSAPermission("list", "", "services") + checkSAPermission("list", "", "configmaps") + checkSAPermission("list", "", "secrets") + checkSAPermission("list", "", "namespaces") + checkSAPermission("get", "", "nodes") + checkSAPermission("list", "", "events") + checkSAPermission("list", "", "persistentvolumes") + checkSAPermission("list", "", "persistentvolumeclaims") + + ginkgo.GinkgoWriter.Println("✅ All permission checks passed") + }) + + ginkgo.It("can collect node system logs from cluster nodes", func(ctx context.Context) { + mustGatherName := "test-node-logs-collection" + + ginkgo.By("ensuring clean state before test") + _ = oc.Delete(ctx, &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + }) + _ = oc.Delete(ctx, &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + }) + Eventually(func() bool { + err := oc.Get(ctx, mustGatherName, namespace, &mustgatherv1alpha1.MustGather{}) + return errors.IsNotFound(err) + }).WithTimeout(60 * time.Second).Should(BeTrue()) + + ginkgo.By("verifying cluster has nodes to collect logs from") + nodes := &corev1.NodeList{} + err := oc.List(ctx, nodes) + Expect(err).ShouldNot(HaveOccurred(), "should be able to list nodes") + Expect(len(nodes.Items)).Should(BeNumerically(">", 0), "cluster must have at least one node") + + nodeCount := len(nodes.Items) + ginkgo.GinkgoWriter.Printf("Found %d nodes in cluster for log collection\n", nodeCount) + + // Log node details + for i, node := range nodes.Items { + if i < 3 { // Only log first 3 nodes to avoid clutter + ginkgo.GinkgoWriter.Printf(" Node %d: %s (roles: %s)\n", + i+1, node.Name, getNodeRoles(node)) + } + } + + ginkgo.By("verifying must-gather-admin SA can access nodes") + checkSAPermission := func(verb, group, resource string) { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: fmt.Sprintf("system:serviceaccount:%s:must-gather-admin", namespace), + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: group, + Resource: resource, + }, + }, + } + err := oc.Create(ctx, sar) + Expect(err).ShouldNot(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), + "SA must be able to %s %s for node log collection", verb, resource) + ginkgo.GinkgoWriter.Printf(" ✓ SA can %s %s\n", verb, resource) + } + + // Verify permissions needed for node log collection + checkSAPermission("get", "", "nodes") + checkSAPermission("list", "", "nodes") + checkSAPermission("get", "", "nodes/log") // Node logs + checkSAPermission("get", "", "nodes/proxy") // Node proxy for system logs + checkSAPermission("list", "", "pods") // To see what's running on nodes + checkSAPermission("get", "", "pods/log") // Pod logs on nodes + + ginkgo.By("creating MustGather CR to collect node logs") + mustgather := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: namespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "must-gather-admin", + RetainResourcesOnCompletion: boolPtr(true), + }, + } + + err = oc.Create(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather CR") + + // Ensure cleanup + ginkgo.DeferCleanup(func(ctx context.Context) { + ginkgo.GinkgoWriter.Println("Cleaning up node logs collection test") + _ = oc.Delete(ctx, mustgather) + _ = oc.Delete(ctx, &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + }) + }, ctx) + + ginkgo.By("verifying Job is created for node log collection") + job := &batchv1.Job{} + Eventually(func() error { + return oc.Get(ctx, mustGatherName, namespace, job) + }).WithTimeout(2 * time.Minute).WithPolling(3 * time.Second).Should(Succeed()) + + ginkgo.By("verifying Job uses privileged service account") + Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), + "Job must use must-gather-admin SA which has node access") + + ginkgo.By("waiting for gather pod to be created and scheduled") + var gatherPod *corev1.Pod + Eventually(func() bool { + podList := &corev1.PodList{} + if err := oc.WithNamespace(namespace).List(ctx, podList); err != nil { + return false + } + for i := range podList.Items { + if podList.Items[i].Labels["job-name"] == mustGatherName { + gatherPod = &podList.Items[i] + return true + } + } + return false + }).WithTimeout(3 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) + + ginkgo.GinkgoWriter.Printf("Gather pod created: %s\n", gatherPod.Name) + if gatherPod.Spec.NodeName != "" { + ginkgo.GinkgoWriter.Printf("Gather pod scheduled on node: %s\n", gatherPod.Spec.NodeName) + } + + ginkgo.By("verifying gather pod reaches Running or Completed state") + Eventually(func() corev1.PodPhase { + pod := &corev1.Pod{} + err := oc.Get(ctx, gatherPod.Name, namespace, pod) + if err != nil { + return corev1.PodUnknown + } + return pod.Status.Phase + }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should( + Or(Equal(corev1.PodRunning), Equal(corev1.PodSucceeded)), + "Pod should reach Running or Succeeded state") + + ginkgo.By("verifying gather container is collecting data") + // Give it time to actually collect logs + time.Sleep(60 * time.Second) + + pod := &corev1.Pod{} + err = oc.Get(ctx, gatherPod.Name, namespace, pod) + Expect(err).ShouldNot(HaveOccurred()) + + // Check gather container status + for _, cs := range pod.Status.ContainerStatuses { + if cs.Name == "gather" { + ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", + cs.Ready, cs.RestartCount) + + if cs.State.Running != nil { + ginkgo.GinkgoWriter.Println("✅ Gather container is actively collecting logs") + } + + if cs.State.Terminated != nil { + ginkgo.GinkgoWriter.Printf("Gather terminated - ExitCode: %d, Reason: %s\n", + cs.State.Terminated.ExitCode, cs.State.Terminated.Reason) + + if cs.State.Terminated.ExitCode == 0 { + ginkgo.GinkgoWriter.Println("✅ Gather completed successfully") + } + } + + // Verify container didn't crash + Expect(cs.RestartCount).Should(BeNumerically("<=", 2), + "Gather container should not be crash-looping") + } + } + + ginkgo.By("checking for node-related events") + events := &corev1.EventList{} + err = oc.WithNamespace(namespace).List(ctx, events) + Expect(err).ShouldNot(HaveOccurred()) + + permissionErrors := []string{} + nodeAccessErrors := []string{} + for _, event := range events.Items { + if event.InvolvedObject.Name == mustGatherName || + event.InvolvedObject.Name == gatherPod.Name { + msg := strings.ToLower(event.Message) + + if strings.Contains(msg, "forbidden") || strings.Contains(msg, "unauthorized") { + permissionErrors = append(permissionErrors, event.Message) + } + + if strings.Contains(msg, "node") && strings.Contains(msg, "error") { + nodeAccessErrors = append(nodeAccessErrors, event.Message) + } + } + } + + if len(permissionErrors) > 0 { + ginkgo.GinkgoWriter.Println("⚠️ Permission errors detected:") + for _, err := range permissionErrors { + ginkgo.GinkgoWriter.Printf(" - %s\n", err) + } + } + Expect(permissionErrors).To(BeEmpty(), "Should not have permission errors") + + ginkgo.By("waiting for must-gather Job to complete") + Eventually(func() int32 { + _ = oc.Get(ctx, mustGatherName, namespace, job) + return job.Status.Succeeded + job.Status.Failed + }).WithTimeout(15*time.Minute).WithPolling(15*time.Second).Should( + BeNumerically(">", 0), "Job should eventually complete") + + ginkgo.By("verifying Job completion status") + err = oc.Get(ctx, mustGatherName, namespace, job) + Expect(err).ShouldNot(HaveOccurred()) + ginkgo.GinkgoWriter.Printf("Job Status - Active: %d, Succeeded: %d, Failed: %d\n", + job.Status.Active, job.Status.Succeeded, job.Status.Failed) + + ginkgo.By("verifying MustGather CR status reflects completion") + createdMG := &mustgatherv1alpha1.MustGather{} + Eventually(func() bool { + _ = oc.Get(ctx, mustGatherName, namespace, createdMG) + return createdMG.Status.Status != "" + }).WithTimeout(2 * time.Minute).Should(BeTrue()) + + ginkgo.By("checking final status of node log collection") + Expect(createdMG.Status.Status).Should(Or(Equal("Completed"), Equal("Failed"))) + ginkgo.GinkgoWriter.Printf("MustGather Status: %s - %s\n", + createdMG.Status.Status, createdMG.Status.Reason) + + if createdMG.Status.Status == "Completed" { + Expect(createdMG.Status.Completed).To(BeTrue()) + ginkgo.GinkgoWriter.Printf("✅ Successfully collected node logs from %d nodes\n", nodeCount) + ginkgo.GinkgoWriter.Println(" Node logs include: kubelet, container runtime, system logs (journalctl)") + } else { + ginkgo.GinkgoWriter.Printf("⚠️ Node log collection failed: %s\n", createdMG.Status.Reason) + } + }) + + ginkgo.It("can collect CRD definitions and custom resource instances", func(ctx context.Context) { + mustGatherName := "test-crd-collection" + + ginkgo.By("ensuring clean state before test") + _ = oc.Delete(ctx, &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + }) + _ = oc.Delete(ctx, &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + }) + Eventually(func() bool { + err := oc.Get(ctx, mustGatherName, namespace, &mustgatherv1alpha1.MustGather{}) + return errors.IsNotFound(err) + }).WithTimeout(60 * time.Second).Should(BeTrue()) + + ginkgo.By("verifying CRDs exist in the cluster") + crdList := &apiextensionsv1.CustomResourceDefinitionList{} + err := oc.List(ctx, crdList) + Expect(err).ShouldNot(HaveOccurred(), "should be able to list CRDs") + Expect(len(crdList.Items)).Should(BeNumerically(">", 0), "cluster should have CRDs") + + ginkgo.GinkgoWriter.Printf("Found %d CRDs in cluster\n", len(crdList.Items)) + + // Find the MustGather CRD as an example + var mustGatherCRD *apiextensionsv1.CustomResourceDefinition + for i := range crdList.Items { + if crdList.Items[i].Name == "mustgathers.operator.openshift.io" { + mustGatherCRD = &crdList.Items[i] + ginkgo.GinkgoWriter.Printf("Found MustGather CRD: %s (group: %s, version: %s)\n", + mustGatherCRD.Name, + mustGatherCRD.Spec.Group, + mustGatherCRD.Spec.Versions[0].Name) + break + } + } + Expect(mustGatherCRD).ToNot(BeNil(), "MustGather CRD should exist") + + ginkgo.By("verifying must-gather-admin SA can access CRDs and custom resources") + checkSAPermission := func(verb, group, resource string) { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: fmt.Sprintf("system:serviceaccount:%s:must-gather-admin", namespace), + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: group, + Resource: resource, + }, + }, + } + err := oc.Create(ctx, sar) + Expect(err).ShouldNot(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), + "SA must be able to %s %s.%s for CRD collection", verb, resource, group) + ginkgo.GinkgoWriter.Printf(" ✓ SA can %s %s.%s\n", verb, resource, group) + } + + // Check permissions for CRD access + checkSAPermission("list", "apiextensions.k8s.io", "customresourcedefinitions") + checkSAPermission("get", "apiextensions.k8s.io", "customresourcedefinitions") + + // Check permissions for MustGather custom resources + checkSAPermission("list", "operator.openshift.io", "mustgathers") + checkSAPermission("get", "operator.openshift.io", "mustgathers") + + ginkgo.By("verifying custom resource instances exist") + mustGatherList := &mustgatherv1alpha1.MustGatherList{} + err = oc.List(ctx, mustGatherList) + Expect(err).ShouldNot(HaveOccurred(), "should be able to list MustGather CRs") + ginkgo.GinkgoWriter.Printf("Found %d MustGather CR instances in cluster\n", len(mustGatherList.Items)) + + ginkgo.By("creating a test MustGather CR instance for collection") + testCR := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cr-instance", + Namespace: namespace, + Labels: map[string]string{ + "test": "crd-collection", + "app": "must-gather-operator-e2e", + }, + Annotations: map[string]string{ + "description": "Test CR for e2e CRD collection validation", + }, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "must-gather-admin", + }, + } + + err = oc.Create(ctx, testCR) + Expect(err).ShouldNot(HaveOccurred(), "should be able to create test CR") + + // Cleanup test CR after test + defer func() { + _ = oc.Delete(ctx, testCR) + }() + + ginkgo.By("verifying the test CR was created successfully") + createdCR := &mustgatherv1alpha1.MustGather{} + err = oc.Get(ctx, "test-cr-instance", namespace, createdCR) + Expect(err).ShouldNot(HaveOccurred()) + Expect(createdCR.Labels["test"]).To(Equal("crd-collection")) + ginkgo.GinkgoWriter.Printf("✅ Test CR created: %s with labels: %v\n", + createdCR.Name, createdCR.Labels) + + ginkgo.By("creating MustGather to collect CRDs and custom resources") + mustgather := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: namespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "must-gather-admin", + RetainResourcesOnCompletion: boolPtr(true), + }, + } + + err = oc.Create(ctx, mustgather) + Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather CR") + + // Ensure cleanup + ginkgo.DeferCleanup(func(ctx context.Context) { + ginkgo.GinkgoWriter.Println("Cleaning up CRD collection test") + _ = oc.Delete(ctx, mustgather) + _ = oc.Delete(ctx, &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + }) + }, ctx) + + ginkgo.By("verifying Job is created for CRD collection") + job := &batchv1.Job{} + Eventually(func() error { + return oc.Get(ctx, mustGatherName, namespace, job) + }).WithTimeout(2 * time.Minute).WithPolling(3 * time.Second).Should(Succeed()) + + ginkgo.By("verifying Job uses service account with CRD access") + Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), + "Job must use SA with permissions to access CRDs and custom resources") + + ginkgo.By("waiting for gather pod to be created") + var gatherPod *corev1.Pod + Eventually(func() bool { + podList := &corev1.PodList{} + if err := oc.WithNamespace(namespace).List(ctx, podList); err != nil { + return false + } + for i := range podList.Items { + if podList.Items[i].Labels["job-name"] == mustGatherName { + gatherPod = &podList.Items[i] + return true + } + } + return false + }).WithTimeout(3 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) + + ginkgo.By("verifying gather pod starts collecting data") + Eventually(func() corev1.PodPhase { + pod := &corev1.Pod{} + err := oc.Get(ctx, gatherPod.Name, namespace, pod) + if err != nil { + return corev1.PodUnknown + } + return pod.Status.Phase + }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should( + Or(Equal(corev1.PodRunning), Equal(corev1.PodSucceeded)), + "Pod should reach Running or Succeeded state") + + ginkgo.By("verifying must-gather can list CRDs during collection") + // This is verified by checking that the pod started successfully + // If SA lacked CRD permissions, collection would fail + pod := &corev1.Pod{} + err = oc.Get(ctx, gatherPod.Name, namespace, pod) + Expect(err).ShouldNot(HaveOccurred()) + + for _, cs := range pod.Status.ContainerStatuses { + if cs.Name == "gather" { + ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", + cs.Ready, cs.RestartCount) + + // Verify container is not failing due to permission issues + Expect(cs.RestartCount).Should(BeNumerically("<=", 2), + "Gather should not be crash-looping (would indicate permission issues)") + + if cs.State.Waiting != nil && strings.Contains( + strings.ToLower(cs.State.Waiting.Reason), "crashloopbackoff") { + ginkgo.Fail("Gather container in CrashLoopBackOff - likely permission issue with CRD access") + } + } + } + + ginkgo.By("checking for CRD-related permission errors in events") + events := &corev1.EventList{} + err = oc.WithNamespace(namespace).List(ctx, events) + Expect(err).ShouldNot(HaveOccurred()) + + crdPermissionErrors := []string{} + for _, event := range events.Items { + if event.InvolvedObject.Name == mustGatherName || + event.InvolvedObject.Name == gatherPod.Name { + msg := strings.ToLower(event.Message) + + if (strings.Contains(msg, "customresourcedefinition") || + strings.Contains(msg, "crd")) && + (strings.Contains(msg, "forbidden") || strings.Contains(msg, "unauthorized")) { + crdPermissionErrors = append(crdPermissionErrors, event.Message) + } + } + } + + if len(crdPermissionErrors) > 0 { + ginkgo.GinkgoWriter.Println("⚠️ CRD permission errors detected:") + for _, err := range crdPermissionErrors { + ginkgo.GinkgoWriter.Printf(" - %s\n", err) + } + } + Expect(crdPermissionErrors).To(BeEmpty(), "Should not have CRD permission errors") + + ginkgo.By("waiting for must-gather to complete") + Eventually(func() int32 { + _ = oc.Get(ctx, mustGatherName, namespace, job) + return job.Status.Succeeded + job.Status.Failed + }).WithTimeout(15*time.Minute).WithPolling(15*time.Second).Should( + BeNumerically(">", 0), "Job should eventually complete") + + ginkgo.By("verifying Job completion status") + err = oc.Get(ctx, mustGatherName, namespace, job) + Expect(err).ShouldNot(HaveOccurred()) + ginkgo.GinkgoWriter.Printf("Job Status - Active: %d, Succeeded: %d, Failed: %d\n", + job.Status.Active, job.Status.Succeeded, job.Status.Failed) + + ginkgo.By("verifying MustGather CR status reflects completion") + completedMG := &mustgatherv1alpha1.MustGather{} + Eventually(func() bool { + _ = oc.Get(ctx, mustGatherName, namespace, completedMG) + return completedMG.Status.Status != "" + }).WithTimeout(2 * time.Minute).Should(BeTrue()) + + ginkgo.By("checking final status of CRD collection") + Expect(completedMG.Status.Status).Should(Or(Equal("Completed"), Equal("Failed"))) + ginkgo.GinkgoWriter.Printf("MustGather Status: %s - %s\n", + completedMG.Status.Status, completedMG.Status.Reason) + + if completedMG.Status.Status == "Completed" { + Expect(completedMG.Status.Completed).To(BeTrue()) + ginkgo.GinkgoWriter.Println("✅ Successfully collected CRD definitions and custom resource instances") + ginkgo.GinkgoWriter.Printf(" CRDs collected: %d CRDs in cluster\n", len(crdList.Items)) + ginkgo.GinkgoWriter.Printf(" Custom resources collected: MustGather CRs and other custom resources\n") + ginkgo.GinkgoWriter.Println(" This includes CRD schemas, CR instances, and their status/spec") + } else { + ginkgo.GinkgoWriter.Printf("⚠️ CRD collection failed: %s\n", completedMG.Status.Reason) + } + + ginkgo.By("verifying test CR still exists after collection") + err = oc.Get(ctx, "test-cr-instance", namespace, createdCR) + Expect(err).ShouldNot(HaveOccurred(), "test CR should still exist after must-gather collection") + ginkgo.GinkgoWriter.Println("✅ CRD collection is non-destructive - test CR preserved") + }) + + /*ginkgo.DescribeTable("MustGather can be created", func(ctx context.Context, user, group string) { client, err := oc.Impersonate(user, group) Expect(err).ShouldNot(HaveOccurred(), "unable to impersonate %s user in %s group", user, group) @@ -84,12 +889,15 @@ var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { Namespace: namespace, }, Spec: mustgatherv1alpha1.MustGatherSpec{ - CaseID: "0000000", - CaseManagementAccountSecretRef: corev1.LocalObjectReference{ - Name: "case-management-creds", - }, - ServiceAccountRef: corev1.LocalObjectReference{ - Name: "must-gather-admin", + ServiceAccountName: "must-gather-admin", + UploadTarget: &mustgatherv1alpha1.UploadTargetSpec{ + Type: mustgatherv1alpha1.UploadTypeSFTP, + SFTP: &mustgatherv1alpha1.SFTPSpec{ + CaseID: "0000000", + CaseManagementAccountSecretRef: corev1.LocalObjectReference{ + Name: "case-management-creds", + }, + }, }, }, } @@ -104,11 +912,33 @@ var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { }, ginkgo.Entry("by backplane-cluster-admin", "backplane-cluster-admin", ""), ginkgo.Entry("by openshift-backplane-cee", "test@redhat.com", "system:serviceaccounts:openshift-backplane-cee"), - ) + )*/ - ginkgo.PIt("can be upgraded", func(ctx context.Context) { + /*ginkgo.PIt("can be upgraded", func(ctx context.Context) { ginkgo.By("forcing operator upgrade") err := oc.UpgradeOperator(ctx, operator, namespace) Expect(err).NotTo(HaveOccurred(), "operator upgrade failed") - }) + })*/ }) + +// Helper function to extract node roles from labels +func getNodeRoles(node corev1.Node) string { + roles := []string{} + for label := range node.Labels { + if strings.HasPrefix(label, "node-role.kubernetes.io/") { + role := strings.TrimPrefix(label, "node-role.kubernetes.io/") + if role != "" { + roles = append(roles, role) + } + } + } + if len(roles) == 0 { + return "none" + } + return strings.Join(roles, ", ") +} + +// Helper function to convert bool to pointer +func boolPtr(b bool) *bool { + return &b +} diff --git a/test/must-gather.yaml b/test/must-gather.yaml index 98796a7a2..ea11838dc 100644 --- a/test/must-gather.yaml +++ b/test/must-gather.yaml @@ -4,8 +4,11 @@ metadata: name: example-mustgather spec: # Add fields here - caseID: '02527285' - caseManagementAccountSecretRef: - name: case-management-creds serviceAccountRef: name: must-gather-admin + uploadTarget: + type: SFTP + sftp: + caseID: '02527285' + caseManagementAccountSecretRef: + name: case-management-creds From 787bdd45fc0180fb04541c2cc35af31c9eade754 Mon Sep 17 00:00:00 2001 From: Praveen Jaya Kumar Date: Mon, 17 Nov 2025 14:29:27 +0530 Subject: [PATCH 02/10] add the adminClient and nonAdminClient test cases --- test/e2e/must_gather_operator_tests.go | 1915 ++++++++++++++---------- 1 file changed, 1107 insertions(+), 808 deletions(-) diff --git a/test/e2e/must_gather_operator_tests.go b/test/e2e/must_gather_operator_tests.go index fac3821f9..448852bb2 100644 --- a/test/e2e/must_gather_operator_tests.go +++ b/test/e2e/must_gather_operator_tests.go @@ -13,932 +13,1231 @@ import ( "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" mustgatherv1alpha1 "github.com/openshift/must-gather-operator/api/v1alpha1" - "github.com/openshift/osde2e-common/pkg/clients/openshift" - . "github.com/openshift/osde2e-common/pkg/gomega/assertions" - . "github.com/openshift/osde2e-common/pkg/gomega/matchers" + appsv1 "k8s.io/api/apps/v1" authorizationv1 "k8s.io/api/authorization/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/log" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" ) -var _ = ginkgo.Describe("must-gather-operator", ginkgo.Ordered, func() { - var ( - oc *openshift.Client - operator = "must-gather-operator" - namespace = operator - service = operator - // rolePrefix = operator - clusterRolePrefix = operator - ) +// Test suite constants +const ( + nonAdminUser = "openshift-test" + testNamespace = "must-gather-e2e-non-admin" + nonAdminCRRoleName = "must-gather-cr-full-access" + nonAdminCRBindName = "must-gather-cr-binding" + ServiceAccount = "must-gather-sa" + SARoleName = "sa-reader-role" + SARoleBindName = "sa-reader-binding" + testTimeout = time.Second * 30 + testInterval = time.Millisecond * 250 +) + +// Test suite variables +var ( + testCtx context.Context + testScheme *runtime.Scheme + adminRestConfig *rest.Config + adminClient client.Client + nonAdminClient client.Client + setupComplete bool +) + +var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() { + + // BeforeAll - Admin Setup Phase (Steps 1-5 from doc) + ginkgo.BeforeAll(func() { + testCtx = context.Background() + testScheme = runtime.NewScheme() - ginkgo.BeforeEach(func(ctx context.Context) { - log.SetLogger(ginkgo.GinkgoLogr) + // Register schemes + Expect(mustgatherv1alpha1.AddToScheme(testScheme)).To(Succeed()) + Expect(appsv1.AddToScheme(testScheme)).To(Succeed()) + Expect(corev1.AddToScheme(testScheme)).To(Succeed()) + Expect(rbacv1.AddToScheme(testScheme)).To(Succeed()) + Expect(batchv1.AddToScheme(testScheme)).To(Succeed()) + Expect(authorizationv1.AddToScheme(testScheme)).To(Succeed()) + ginkgo.By("STEP 1: Admin sets up clients and ensures operator is installed") var err error - oc, err = openshift.New(ginkgo.GinkgoLogr) - Expect(err).ShouldNot(HaveOccurred(), "unable to setup openshift client") - Expect(mustgatherv1alpha1.AddToScheme(oc.GetScheme())).ShouldNot(HaveOccurred(), "failed to register mustgatherv1alpha1 scheme") - Expect(apiextensionsv1.AddToScheme(oc.GetScheme())).ShouldNot(HaveOccurred(), "failed to register apiextensionsv1 scheme") - }) + adminRestConfig, err = config.GetConfig() + Expect(err).NotTo(HaveOccurred(), "Failed to get admin kube config") + + adminClient, err = client.New(adminRestConfig, client.Options{Scheme: testScheme}) + Expect(err).NotTo(HaveOccurred(), "Failed to create admin typed client") + + ginkgo.By("Verifying must-gather-operator is deployed and available") + verifyOperatorDeployment() + + ginkgo.By("STEP 2: Admin creates test namespace") + createTestNamespace() - ginkgo.It("is installed", func(ctx context.Context) { - ginkgo.By("checking the namespace exists") - err := oc.Get(ctx, namespace, "", &corev1.Namespace{}) - Expect(err).ShouldNot(HaveOccurred(), "namespace %s not found", namespace) - - ginkgo.By("checking the operator service account exists") - err = oc.Get(ctx, operator, namespace, &corev1.ServiceAccount{}) - Expect(err).ShouldNot(HaveOccurred(), "service account %s/%s not found", namespace, operator) - - ginkgo.By("checking the must-gather-admin service account exists") - err = oc.Get(ctx, "must-gather-admin", namespace, &corev1.ServiceAccount{}) - Expect(err).ShouldNot(HaveOccurred(), "service account %s/must-gather-admin not found", namespace) - - // ginkgo.By("checking the role exists") - // var roles rbacv1.RoleList - // err = oc.WithNamespace(namespace).List(ctx, &roles) - // Expect(err).ShouldNot(HaveOccurred(), "failed to list roles") - // Expect(&roles).Should(ContainItemWithPrefix(rolePrefix), "unable to find roles with prefix %s", rolePrefix) - - // ginkgo.By("checking the rolebinding exists") - // var rolebindings rbacv1.RoleBindingList - // err = oc.List(ctx, &rolebindings) - // Expect(err).ShouldNot(HaveOccurred(), "failed to list rolebindings") - // Expect(&rolebindings).Should(ContainItemWithPrefix(rolePrefix), "unable to find rolebindings with prefix %s", rolePrefix) - - ginkgo.By("checking the clusterrole exists") - var clusterRoles rbacv1.ClusterRoleList - err = oc.List(ctx, &clusterRoles) - Expect(err).ShouldNot(HaveOccurred(), "failed to list clusterroles") - Expect(&clusterRoles).Should(ContainItemWithPrefix(clusterRolePrefix), "unable to find cluster role with prefix %s", clusterRolePrefix) - - ginkgo.By("checking the clusterrolebinding exists") - var clusterRoleBindings rbacv1.ClusterRoleBindingList - err = oc.List(ctx, &clusterRoleBindings) - Expect(err).ShouldNot(HaveOccurred(), "unable to list clusterrolebindings") - Expect(&clusterRoleBindings).Should(ContainItemWithPrefix(clusterRolePrefix), "unable to find clusterrolebinding with prefix %s", clusterRolePrefix) - ginkgo.By("checking the service exists") - err = oc.Get(ctx, service, namespace, &corev1.Service{}) - Expect(err).ShouldNot(HaveOccurred(), "service %s/%s not found", namespace, service) - - ginkgo.By("checking the deployment exists and is available") - EventuallyDeployment(ctx, oc, operator, namespace).Should(BeAvailable()) + ginkgo.By("STEP 3: Admin defines privileges for MustGather CRs") + createNonAdminClusterRole() + + ginkgo.By("STEP 4: Admin binds the non-admin user to the ClusterRole") + createNonAdminClusterRoleBinding() + + ginkgo.By("STEP 5: Admin provides a ServiceAccount for execution") + createServiceAccount() + createSARBACForMustGather() + + ginkgo.By("Initializing non-admin client for tests") + nonAdminClient = createNonAdminClient() + + setupComplete = true + ginkgo.GinkgoWriter.Println("✅ Admin setup complete - RBAC and ServiceAccount configured") }) - ginkgo.It("has required service accounts with proper permissions", func(ctx context.Context) { - ginkgo.By("verifying must-gather-operator service account exists") - operatorSA := &corev1.ServiceAccount{} - err := oc.Get(ctx, operator, namespace, operatorSA) - Expect(err).ShouldNot(HaveOccurred(), "operator service account not found") - - ginkgo.By("verifying must-gather-admin service account exists") - adminSA := &corev1.ServiceAccount{} - err = oc.Get(ctx, "must-gather-admin", namespace, adminSA) - Expect(err).ShouldNot(HaveOccurred(), "must-gather-admin service account not found") - - ginkgo.By("verifying must-gather-operator clusterrole exists") - operatorCR := &rbacv1.ClusterRole{} - err = oc.Get(ctx, operator, "", operatorCR) - Expect(err).ShouldNot(HaveOccurred(), "operator clusterrole not found") - Expect(len(operatorCR.Rules)).Should(BeNumerically(">", 0), "operator clusterrole has no rules") - - ginkgo.By("verifying must-gather-operator has permissions for jobs and secrets") - hasJobPermissions := false - hasSecretPermissions := false - for _, rule := range operatorCR.Rules { - for _, apiGroup := range rule.APIGroups { - if apiGroup == "batch" { - for _, resource := range rule.Resources { - if resource == "jobs" { - hasJobPermissions = true - } - } - } - if apiGroup == "" { // core API group - for _, resource := range rule.Resources { - if resource == "secrets" { - hasSecretPermissions = true - } - } - } - } + // AfterAll - Cleanup + ginkgo.AfterAll(func() { + if !setupComplete { + ginkgo.GinkgoWriter.Println("⚠️ Setup was not complete, skipping cleanup") + return } - Expect(hasJobPermissions).To(BeTrue(), "operator clusterrole missing job permissions") - Expect(hasSecretPermissions).To(BeTrue(), "operator clusterrole missing secret permissions") - - ginkgo.By("verifying must-gather-admin clusterrole exists") - adminCR := &rbacv1.ClusterRole{} - err = oc.Get(ctx, "must-gather-admin", "", adminCR) - Expect(err).ShouldNot(HaveOccurred(), "must-gather-admin clusterrole not found") - Expect(len(adminCR.Rules)).Should(BeNumerically(">", 0), "must-gather-admin clusterrole has no rules") - - ginkgo.By("verifying must-gather-operator clusterrolebinding exists") - operatorCRB := &rbacv1.ClusterRoleBinding{} - err = oc.Get(ctx, operator, "", operatorCRB) - Expect(err).ShouldNot(HaveOccurred(), "operator clusterrolebinding not found") - Expect(operatorCRB.RoleRef.Name).To(Equal(operator), "clusterrolebinding references wrong role") - Expect(len(operatorCRB.Subjects)).Should(BeNumerically(">", 0), "clusterrolebinding has no subjects") - - ginkgo.By("verifying must-gather-admin clusterrolebinding exists") - adminCRB := &rbacv1.ClusterRoleBinding{} - err = oc.Get(ctx, "must-gather-admin", "", adminCRB) - Expect(err).ShouldNot(HaveOccurred(), "must-gather-admin clusterrolebinding not found") - Expect(adminCRB.RoleRef.Name).To(Equal("must-gather-admin"), "admin clusterrolebinding references wrong role") - Expect(len(adminCRB.Subjects)).Should(BeNumerically(">", 0), "admin clusterrolebinding has no subjects") - - ginkgo.By("verifying clusterrolebinding references correct service account") - foundAdminSA := false - for _, subject := range adminCRB.Subjects { - if subject.Kind == "ServiceAccount" && subject.Name == "must-gather-admin" && subject.Namespace == namespace { - foundAdminSA = true - break - } - } - Expect(foundAdminSA).To(BeTrue(), "must-gather-admin clusterrolebinding does not reference must-gather-admin service account") + + ginkgo.By("CLEANUP: Removing all test resources") + cleanupTestResources() }) - ginkgo.It("creates a Job with correct service account when MustGather is created", func(ctx context.Context) { - mustGatherName := "test-mustgather-sa" + // Test Cases - ginkgo.By("creating a MustGather CR with must-gather-admin service account") - mustgather := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - Name: mustGatherName, - Namespace: namespace, - }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "must-gather-admin", - // Not specifying UploadTarget to avoid requiring secrets - }, - } + ginkgo.Context("Admin Setup Validation", func() { + ginkgo.It("should have created the test namespace successfully", func() { + ns := &corev1.Namespace{} + err := adminClient.Get(testCtx, client.ObjectKey{Name: testNamespace}, ns) + Expect(err).NotTo(HaveOccurred(), "Test namespace should exist") + Expect(ns.Status.Phase).To(Equal(corev1.NamespaceActive), "Namespace should be active") + }) - err := oc.Create(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather") - - ginkgo.By("verifying a Job is created with the same name") - job := &batchv1.Job{} - Eventually(func() error { - return oc.Get(ctx, mustGatherName, namespace, job) - }).WithTimeout(30*time.Second).WithPolling(2*time.Second).Should(Succeed(), "Job should be created") - - ginkgo.By("verifying the Job uses the correct service account") - Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), "Job should use must-gather-admin service account") - - ginkgo.By("verifying the Job has the gather container") - Expect(len(job.Spec.Template.Spec.Containers)).Should(BeNumerically(">=", 1), "Job should have at least one container") - hasGatherContainer := false - for _, container := range job.Spec.Template.Spec.Containers { - if container.Name == "gather" { - hasGatherContainer = true - break + ginkgo.It("should have created ClusterRole for non-admin MustGather CR access", func() { + cr := &rbacv1.ClusterRole{} + err := adminClient.Get(testCtx, client.ObjectKey{Name: nonAdminCRRoleName}, cr) + Expect(err).NotTo(HaveOccurred(), "ClusterRole should exist") + Expect(len(cr.Rules)).Should(BeNumerically(">", 0), "ClusterRole should have rules") + + // Verify it has mustgathers permission + hasMustGatherPermission := false + for _, rule := range cr.Rules { + for _, apiGroup := range rule.APIGroups { + if apiGroup == "operator.openshift.io" { + for _, resource := range rule.Resources { + if resource == "mustgathers" { + hasMustGatherPermission = true + break + } + } + } + } } - } - Expect(hasGatherContainer).To(BeTrue(), "Job should have a gather container") - - ginkgo.By("verifying the Job has required volumes") - hasOutputVolume := false - for _, volume := range job.Spec.Template.Spec.Volumes { - if volume.Name == "must-gather-output" { - hasOutputVolume = true - break + Expect(hasMustGatherPermission).To(BeTrue(), "ClusterRole should grant mustgathers permissions") + }) + + ginkgo.It("should have created ClusterRoleBinding linking non-admin user to ClusterRole", func() { + crb := &rbacv1.ClusterRoleBinding{} + err := adminClient.Get(testCtx, client.ObjectKey{Name: nonAdminCRBindName}, crb) + Expect(err).NotTo(HaveOccurred(), "ClusterRoleBinding should exist") + Expect(crb.RoleRef.Name).To(Equal(nonAdminCRRoleName), "Should reference correct ClusterRole") + + // Verify it binds to non-admin user + hasNonAdminUser := false + for _, subject := range crb.Subjects { + if subject.Kind == "User" && subject.Name == nonAdminUser { + hasNonAdminUser = true + break + } } - } - Expect(hasOutputVolume).To(BeTrue(), "Job should have must-gather-output volume") + Expect(hasNonAdminUser).To(BeTrue(), "ClusterRoleBinding should include non-admin user") + }) - ginkgo.By("cleaning up the MustGather CR") - err = oc.Delete(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather") + ginkgo.It("should have created ServiceAccount", func() { + sa := &corev1.ServiceAccount{} + err := adminClient.Get(testCtx, client.ObjectKey{ + Name: ServiceAccount, + Namespace: testNamespace, + }, sa) + Expect(err).NotTo(HaveOccurred(), " ServiceAccount should exist") + }) - ginkgo.By("verifying the Job is eventually cleaned up") - Eventually(func() bool { - err := oc.Get(ctx, mustGatherName, namespace, job) - return errors.IsNotFound(err) - }).WithTimeout(30*time.Second).WithPolling(2*time.Second).Should(BeTrue(), "Job should be cleaned up") + ginkgo.It("should have granted minimal read permissions to ServiceAccount", func() { + cr := &rbacv1.ClusterRole{} + err := adminClient.Get(testCtx, client.ObjectKey{Name: SARoleName}, cr) + Expect(err).NotTo(HaveOccurred(), "ServiceAccount ClusterRole should exist") + + crb := &rbacv1.ClusterRoleBinding{} + err = adminClient.Get(testCtx, client.ObjectKey{Name: SARoleBindName}, crb) + Expect(err).NotTo(HaveOccurred(), "ServiceAccount ClusterRoleBinding should exist") + + // Verify binding references the ServiceAccount + hasSA := false + for _, subject := range crb.Subjects { + if subject.Kind == "ServiceAccount" && + subject.Name == ServiceAccount && + subject.Namespace == testNamespace { + hasSA = true + break + } + } + Expect(hasSA).To(BeTrue(), "ClusterRoleBinding should reference ServiceAccount") + }) }) - ginkgo.It("fails gracefully when using non-existent service account", func(ctx context.Context) { - mustGatherName := "test-mustgather-invalid-sa" + ginkgo.Context("Non-Admin User Permissions", func() { + ginkgo.It("non-admin user should be able to create MustGather CRs (SAR check)", func() { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "create", + Group: "operator.openshift.io", + Resource: "mustgathers", + Namespace: testNamespace, + }, + }, + } + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), "Non-admin should be able to create MustGather CRs") + }) - ginkgo.By("creating a MustGather CR with non-existent service account") - mustgather := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - Name: mustGatherName, - Namespace: namespace, - }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "non-existent-service-account", - }, - } + ginkgo.It("non-admin user can actually create MustGather CR using impersonation", func() { + testMGName := fmt.Sprintf("test-non-admin-mustgather-%d", time.Now().Unix()) - err := oc.Create(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "mustgather CR should be created even with invalid service account") + ginkgo.By("Non-admin user creating MustGather CR") + mg := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: testMGName, + Namespace: testNamespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: ServiceAccount, + }, + } - ginkgo.By("verifying a Job is created") - job := &batchv1.Job{} - Eventually(func() error { - return oc.Get(ctx, mustGatherName, namespace, job) - }).WithTimeout(30*time.Second).WithPolling(2*time.Second).Should(Succeed(), "Job should be created") + err := nonAdminClient.Create(testCtx, mg) + Expect(err).NotTo(HaveOccurred(), "Non-admin user should be able to create MustGather CR") + + ginkgo.By("Verifying MustGather CR was created") + createdMG := &mustgatherv1alpha1.MustGather{} + err = nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: testMGName, + Namespace: testNamespace, + }, createdMG) + Expect(err).NotTo(HaveOccurred()) + Expect(createdMG.Name).To(Equal(testMGName)) + + ginkgo.By("Cleanup: Non-admin user deleting their MustGather CR") + err = nonAdminClient.Delete(testCtx, mg) + Expect(err).NotTo(HaveOccurred(), "Non-admin should be able to delete their own CR") + }) - ginkgo.By("verifying the Job references the non-existent service account") - Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("non-existent-service-account")) + ginkgo.It("non-admin user should be able to get, list and watch MustGather CRs (SAR check)", func() { + for _, verb := range []string{"get", "list", "watch"} { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: "operator.openshift.io", + Resource: "mustgathers", + }, + }, + } + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), + fmt.Sprintf("Non-admin should be able to %s MustGather CRs", verb)) + } + }) - ginkgo.By("verifying the Job fails to create pods due to missing service account") - // The Job should exist but pods will fail to be created - Consistently(func() int32 { - _ = oc.Get(ctx, mustGatherName, namespace, job) - return job.Status.Active - }).WithTimeout(20*time.Second).WithPolling(2*time.Second).Should(Equal(int32(0)), "Job should not have active pods due to missing service account") + ginkgo.It("non-admin user can actually list MustGather CRs using impersonation", func() { + ginkgo.By("Non-admin user listing MustGather CRs in their namespace") + mgList := &mustgatherv1alpha1.MustGatherList{} + err := nonAdminClient.List(testCtx, mgList, client.InNamespace(testNamespace)) + Expect(err).NotTo(HaveOccurred(), "Non-admin should be able to list MustGather CRs") - ginkgo.By("cleaning up the MustGather CR") - err = oc.Delete(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather") - }) + ginkgo.GinkgoWriter.Printf("Non-admin user can see %d MustGather CRs in namespace %s\n", + len(mgList.Items), testNamespace) + }) - ginkgo.It("successfully collects data and completes MustGather lifecycle", func(ctx context.Context) { - mustGatherName := "test-mustgather-complete" + ginkgo.It("non-admin user should be able to monitor Job and Pod status", func() { + for _, verb := range []string{"get", "list", "watch"} { + // Check Jobs permission + sarJob := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: "batch", + Resource: "jobs", + }, + }, + } + err := adminClient.Create(testCtx, sarJob) + Expect(err).NotTo(HaveOccurred()) + Expect(sarJob.Status.Allowed).To(BeTrue(), + fmt.Sprintf("Non-admin should be able to %s jobs", verb)) + + // Check Pods permission + sarPod := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: "", + Resource: "pods", + }, + }, + } + err = adminClient.Create(testCtx, sarPod) + Expect(err).NotTo(HaveOccurred()) + Expect(sarPod.Status.Allowed).To(BeTrue(), + fmt.Sprintf("Non-admin should be able to %s pods", verb)) + } + }) - ginkgo.By("cleaning up any existing MustGather from previous test runs") - existingMG := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - Name: mustGatherName, - Namespace: namespace, - }, - } - _ = oc.Delete(ctx, existingMG) // Ignore error if doesn't exist + ginkgo.It("non-admin user should be able to access pod logs", func() { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "get", + Group: "", + Resource: "pods", + Subresource: "log", + }, + }, + } + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), "Non-admin should be able to get pod logs") + }) - ginkgo.By("creating a MustGather CR for data collection") - mustgather := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - Name: mustGatherName, - Namespace: namespace, - }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "must-gather-admin", - // No upload target to simplify the test - just collect data - }, - } + ginkgo.It("non-admin user should NOT have cluster-admin privileges (SAR check)", func() { + // Verify non-admin cannot delete namespaces + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "delete", + Group: "", + Resource: "namespaces", + }, + }, + } + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeFalse(), + "Non-admin should NOT be able to delete namespaces") - err := oc.Create(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather CR") - - ginkgo.By("verifying the MustGather CR is created and has initial status") - createdMG := &mustgatherv1alpha1.MustGather{} - err = oc.Get(ctx, mustGatherName, namespace, createdMG) - Expect(err).ShouldNot(HaveOccurred(), "failed to get mustgather CR") - Expect(createdMG.Status.Completed).To(BeFalse(), "MustGather should not be completed initially") - - ginkgo.By("verifying a Job is created for must-gather collection") - job := &batchv1.Job{} - Eventually(func() error { - return oc.Get(ctx, mustGatherName, namespace, job) - }).WithTimeout(60*time.Second).WithPolling(2*time.Second).Should(Succeed(), "Job should be created by operator") - - ginkgo.By("verifying the Job is using the correct service account") - Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), "Job must use must-gather-admin service account") - - ginkgo.By("verifying pods are created for the Job") - Eventually(func() int32 { - err := oc.Get(ctx, mustGatherName, namespace, job) - if err != nil { - return 0 + // Verify non-admin cannot create ClusterRoles + sar2 := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "create", + Group: "rbac.authorization.k8s.io", + Resource: "clusterroles", + }, + }, } - return job.Status.Active + job.Status.Succeeded + job.Status.Failed - }).WithTimeout(60*time.Second).WithPolling(3*time.Second).Should(BeNumerically(">", 0), "Job should create at least one pod") + err = adminClient.Create(testCtx, sar2) + Expect(err).NotTo(HaveOccurred()) + Expect(sar2.Status.Allowed).To(BeFalse(), + "Non-admin should NOT be able to create ClusterRoles") + }) - ginkgo.By("waiting for the Job to start running or complete") - Eventually(func() bool { - err := oc.Get(ctx, mustGatherName, namespace, job) - if err != nil { - return false + ginkgo.It("non-admin user CANNOT perform admin operations using impersonation", func() { + ginkgo.By("Attempting to delete a namespace (should fail)") + testNS := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-delete-namespace", + }, } - // Job is running if it has active pods, or it's done if succeeded/failed - return job.Status.Active > 0 || job.Status.Succeeded > 0 || job.Status.Failed > 0 - }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), "Job should start running or complete") - - ginkgo.By("verifying MustGather status is updated by the operator") - Eventually(func() bool { - err := oc.Get(ctx, mustGatherName, namespace, createdMG) - if err != nil { - return false + err := nonAdminClient.Delete(testCtx, testNS) + Expect(err).To(HaveOccurred(), "Non-admin should NOT be able to delete namespaces") + Expect(apierrors.IsForbidden(err)).To(BeTrue(), "Should get Forbidden error") + + ginkgo.By("Attempting to create a ClusterRole (should fail)") + testCR := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-unauthorized-clusterrole", + }, } - // Check if status has been updated (Status field should be set) - return createdMG.Status.Status != "" - }).WithTimeout(10*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), "MustGather status should be updated") - - ginkgo.By("checking final MustGather status") - err = oc.Get(ctx, mustGatherName, namespace, createdMG) - Expect(err).ShouldNot(HaveOccurred(), "failed to get mustgather status") - - // Log the status for debugging - ginkgo.GinkgoWriter.Printf("MustGather Status: %s\n", createdMG.Status.Status) - ginkgo.GinkgoWriter.Printf("MustGather Completed: %v\n", createdMG.Status.Completed) - ginkgo.GinkgoWriter.Printf("MustGather Reason: %s\n", createdMG.Status.Reason) - - // Verify status is either Completed or Failed (both are valid outcomes) - Expect(createdMG.Status.Status).Should(Or(Equal("Completed"), Equal("Failed"), Equal("")), - "MustGather status should be set") - - // If completed, verify the completed flag - if createdMG.Status.Status == "Completed" { - Expect(createdMG.Status.Completed).To(BeTrue(), "Completed flag should be true when status is Completed") - Expect(createdMG.Status.Reason).ToNot(BeEmpty(), "Reason should be populated") - } + err = nonAdminClient.Create(testCtx, testCR) + Expect(err).To(HaveOccurred(), "Non-admin should NOT be able to create ClusterRoles") + Expect(apierrors.IsForbidden(err)).To(BeTrue(), "Should get Forbidden error") - ginkgo.By("cleaning up the MustGather CR") - err = oc.Delete(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather") + ginkgo.GinkgoWriter.Println("✅ Non-admin user correctly blocked from admin operations") + }) }) - ginkgo.It("successfully collects cluster resources", func(ctx context.Context) { - ginkgo.By("verifying must-gather-admin service account has required permissions") + ginkgo.Context("ServiceAccount Permissions", func() { + ginkgo.It("SA should have read access to cluster resources", func() { + saUser := fmt.Sprintf("system:serviceaccount:%s:%s", testNamespace, ServiceAccount) + + checkSAPermission := func(verb, group, resource string) { + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: saUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: verb, + Group: group, + Resource: resource, + }, + }, + } + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), + fmt.Sprintf(" SA should be able to %s %s.%s", verb, resource, group)) + } + + // Check read permissions for must-gather required resources + checkSAPermission("get", "", "nodes") + checkSAPermission("list", "", "nodes") + checkSAPermission("get", "", "pods") + checkSAPermission("list", "", "pods") + checkSAPermission("get", "", "namespaces") + checkSAPermission("list", "", "namespaces") + }) + + ginkgo.It("SA should NOT have write/delete permissions", func() { + saUser := fmt.Sprintf("system:serviceaccount:%s:%s", testNamespace, ServiceAccount) - // Helper function to check SA permissions - checkSAPermission := func(verb, group, resource string) { + // Verify SA cannot delete pods sar := &authorizationv1.SubjectAccessReview{ Spec: authorizationv1.SubjectAccessReviewSpec{ - User: fmt.Sprintf("system:serviceaccount:%s:must-gather-admin", namespace), + User: saUser, ResourceAttributes: &authorizationv1.ResourceAttributes{ - Verb: verb, - Group: group, - Resource: resource, + Verb: "delete", + Group: "", + Resource: "pods", }, }, } - err := oc.Create(ctx, sar) - Expect(err).ShouldNot(HaveOccurred(), "failed to check permissions for %s", resource) + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeFalse(), + " SA should NOT be able to delete pods") - if !sar.Status.Allowed { - ginkgo.GinkgoWriter.Printf("❌ DENIED: must-gather-admin cannot %s %s (reason: %s)\n", - verb, resource, sar.Status.Reason) + // Verify SA cannot create secrets + sar2 := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: saUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "create", + Group: "", + Resource: "secrets", + }, + }, } - Expect(sar.Status.Allowed).To(BeTrue(), - "must-gather-admin SA must be able to %s %s", verb, resource) - - ginkgo.GinkgoWriter.Printf("✅ must-gather-admin can %s %s\n", verb, resource) - } - - // Check all critical permissions - checkSAPermission("list", "", "pods") - checkSAPermission("get", "", "pods") - checkSAPermission("list", "apps", "deployments") - checkSAPermission("list", "", "services") - checkSAPermission("list", "", "configmaps") - checkSAPermission("list", "", "secrets") - checkSAPermission("list", "", "namespaces") - checkSAPermission("get", "", "nodes") - checkSAPermission("list", "", "events") - checkSAPermission("list", "", "persistentvolumes") - checkSAPermission("list", "", "persistentvolumeclaims") - - ginkgo.GinkgoWriter.Println("✅ All permission checks passed") + err = adminClient.Create(testCtx, sar2) + Expect(err).NotTo(HaveOccurred()) + Expect(sar2.Status.Allowed).To(BeFalse(), + " SA should NOT be able to create secrets") + }) }) - ginkgo.It("can collect node system logs from cluster nodes", func(ctx context.Context) { - mustGatherName := "test-node-logs-collection" + ginkgo.Context("Non-Admin User MustGather Collection Flow", func() { + var mustGatherName string + var createdMustGather *mustgatherv1alpha1.MustGather - ginkgo.By("ensuring clean state before test") - _ = oc.Delete(ctx, &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, + ginkgo.BeforeEach(func() { + mustGatherName = fmt.Sprintf("test-non-admin-mg-%d", time.Now().Unix()) }) - _ = oc.Delete(ctx, &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, - }) - Eventually(func() bool { - err := oc.Get(ctx, mustGatherName, namespace, &mustgatherv1alpha1.MustGather{}) - return errors.IsNotFound(err) - }).WithTimeout(60 * time.Second).Should(BeTrue()) - - ginkgo.By("verifying cluster has nodes to collect logs from") - nodes := &corev1.NodeList{} - err := oc.List(ctx, nodes) - Expect(err).ShouldNot(HaveOccurred(), "should be able to list nodes") - Expect(len(nodes.Items)).Should(BeNumerically(">", 0), "cluster must have at least one node") - - nodeCount := len(nodes.Items) - ginkgo.GinkgoWriter.Printf("Found %d nodes in cluster for log collection\n", nodeCount) - - // Log node details - for i, node := range nodes.Items { - if i < 3 { // Only log first 3 nodes to avoid clutter - ginkgo.GinkgoWriter.Printf(" Node %d: %s (roles: %s)\n", - i+1, node.Name, getNodeRoles(node)) + + ginkgo.AfterEach(func() { + if createdMustGather != nil { + ginkgo.By("Cleaning up MustGather CR") + _ = adminClient.Delete(testCtx, createdMustGather) + + // Wait for cleanup + Eventually(func() bool { + err := adminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, &mustgatherv1alpha1.MustGather{}) + return apierrors.IsNotFound(err) + }).WithTimeout(60 * time.Second).WithPolling(2 * time.Second).Should(BeTrue()) } - } + }) - ginkgo.By("verifying must-gather-admin SA can access nodes") - checkSAPermission := func(verb, group, resource string) { - sar := &authorizationv1.SubjectAccessReview{ - Spec: authorizationv1.SubjectAccessReviewSpec{ - User: fmt.Sprintf("system:serviceaccount:%s:must-gather-admin", namespace), - ResourceAttributes: &authorizationv1.ResourceAttributes{ - Verb: verb, - Group: group, - Resource: resource, + ginkgo.It("non-admin user should successfully submit MustGather CR with SA", func() { + ginkgo.By("Non-admin user creating MustGather CR using impersonation") + createdMustGather = &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: testNamespace, + Annotations: map[string]string{ + "test.description": "Non-admin user submitted MustGather", + "test.user": nonAdminUser, }, }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: ServiceAccount, + // Using default must-gather image + }, } - err := oc.Create(ctx, sar) - Expect(err).ShouldNot(HaveOccurred()) - Expect(sar.Status.Allowed).To(BeTrue(), - "SA must be able to %s %s for node log collection", verb, resource) - ginkgo.GinkgoWriter.Printf(" ✓ SA can %s %s\n", verb, resource) - } - // Verify permissions needed for node log collection - checkSAPermission("get", "", "nodes") - checkSAPermission("list", "", "nodes") - checkSAPermission("get", "", "nodes/log") // Node logs - checkSAPermission("get", "", "nodes/proxy") // Node proxy for system logs - checkSAPermission("list", "", "pods") // To see what's running on nodes - checkSAPermission("get", "", "pods/log") // Pod logs on nodes - - ginkgo.By("creating MustGather CR to collect node logs") - mustgather := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ + // Using impersonated non-admin client + err := nonAdminClient.Create(testCtx, createdMustGather) + Expect(err).NotTo(HaveOccurred(), "Non-admin user should be able to create MustGather CR") + + ginkgo.By("Verifying MustGather CR was created successfully") + fetchedMG := &mustgatherv1alpha1.MustGather{} + err = nonAdminClient.Get(testCtx, client.ObjectKey{ Name: mustGatherName, - Namespace: namespace, - }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "must-gather-admin", - RetainResourcesOnCompletion: boolPtr(true), - }, - } + Namespace: testNamespace, + }, fetchedMG) + Expect(err).NotTo(HaveOccurred()) + Expect(fetchedMG.Spec.ServiceAccountName).To(Equal(ServiceAccount)) - err = oc.Create(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather CR") - - // Ensure cleanup - ginkgo.DeferCleanup(func(ctx context.Context) { - ginkgo.GinkgoWriter.Println("Cleaning up node logs collection test") - _ = oc.Delete(ctx, mustgather) - _ = oc.Delete(ctx, &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, - }) - }, ctx) - - ginkgo.By("verifying Job is created for node log collection") - job := &batchv1.Job{} - Eventually(func() error { - return oc.Get(ctx, mustGatherName, namespace, job) - }).WithTimeout(2 * time.Minute).WithPolling(3 * time.Second).Should(Succeed()) - - ginkgo.By("verifying Job uses privileged service account") - Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), - "Job must use must-gather-admin SA which has node access") - - ginkgo.By("waiting for gather pod to be created and scheduled") - var gatherPod *corev1.Pod - Eventually(func() bool { - podList := &corev1.PodList{} - if err := oc.WithNamespace(namespace).List(ctx, podList); err != nil { - return false + ginkgo.GinkgoWriter.Printf("✅ Non-admin user '%s' successfully created MustGather CR: %s\n", + nonAdminUser, mustGatherName) + }) + + ginkgo.It("operator should create Job using ServiceAccount", func() { + ginkgo.By("Non-admin user creating MustGather CR") + createdMustGather = createMustGatherCR(mustGatherName, ServiceAccount) + + ginkgo.By("Waiting for operator to create Job (non-admin user perspective)") + job := &batchv1.Job{} + Eventually(func() error { + err := nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, job) + if err != nil { + // Debug: Check if Job exists with admin client + jobDebug := &batchv1.Job{} + errAdmin := adminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, jobDebug) + if errAdmin == nil { + ginkgo.GinkgoWriter.Printf("⚠️ Job exists but non-admin can't see it (RBAC issue)\n") + } else { + ginkgo.GinkgoWriter.Printf("Job not found by operator yet: %v\n", errAdmin) + } + } + return err + }).WithTimeout(2*time.Minute).WithPolling(3*time.Second).Should(Succeed(), + "Non-admin user should be able to see Job created by operator") + + ginkgo.By("Verifying Job uses ServiceAccount") + Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal(ServiceAccount), + "Job must use the ServiceAccount specified in MustGather CR") + + ginkgo.By("Verifying Job has required specifications") + Expect(len(job.Spec.Template.Spec.Containers)).Should(BeNumerically(">=", 1), + "Job should have at least one container") + + hasGatherContainer := false + for _, container := range job.Spec.Template.Spec.Containers { + if container.Name == "gather" { + hasGatherContainer = true + Expect(container.Image).NotTo(BeEmpty(), "Gather container should have an image") + break + } } - for i := range podList.Items { - if podList.Items[i].Labels["job-name"] == mustGatherName { - gatherPod = &podList.Items[i] - return true + Expect(hasGatherContainer).To(BeTrue(), "Job should have gather container") + + ginkgo.By("Verifying Job has output volume for artifacts") + hasOutputVolume := false + for _, volume := range job.Spec.Template.Spec.Volumes { + if volume.Name == "must-gather-output" { + hasOutputVolume = true + break } } - return false - }).WithTimeout(3 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) + Expect(hasOutputVolume).To(BeTrue(), "Job should have must-gather-output volume") + }) - ginkgo.GinkgoWriter.Printf("Gather pod created: %s\n", gatherPod.Name) - if gatherPod.Spec.NodeName != "" { - ginkgo.GinkgoWriter.Printf("Gather pod scheduled on node: %s\n", gatherPod.Spec.NodeName) - } + ginkgo.It("Pod should gather data and complete successfully", func() { + ginkgo.By("Non-admin user creating MustGather CR with RetainResourcesOnCompletion") + createdMustGather = createMustGatherCRWithRetention(mustGatherName, ServiceAccount) + + ginkgo.By("Waiting for Job to be created") + job := &batchv1.Job{} + Eventually(func() error { + return nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, job) + }).WithTimeout(2 * time.Minute).WithPolling(3 * time.Second).Should(Succeed()) + + ginkgo.By("Waiting for Pod to be created") + var gatherPod *corev1.Pod + Eventually(func() bool { + podList := &corev1.PodList{} + if err := nonAdminClient.List(testCtx, podList, + client.InNamespace(testNamespace), + client.MatchingLabels{"job-name": mustGatherName}); err != nil { + return false + } + if len(podList.Items) > 0 { + gatherPod = &podList.Items[0] + return true + } + return false + }).WithTimeout(3*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), + "Pod should be created by Job") + + ginkgo.By("Verifying Pod uses ServiceAccount") + Expect(gatherPod.Spec.ServiceAccountName).To(Equal(ServiceAccount), + "Pod must use ServiceAccount for data collection") + + ginkgo.By("Verifying Pod is scheduled") + Eventually(func() string { + pod := &corev1.Pod{} + _ = nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: gatherPod.Name, + Namespace: testNamespace, + }, pod) + return pod.Spec.NodeName + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).ShouldNot(BeEmpty(), + "Pod should be scheduled to a node") + + ginkgo.By("Monitoring Pod execution phase") + Eventually(func() corev1.PodPhase { + pod := &corev1.Pod{} + err := nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: gatherPod.Name, + Namespace: testNamespace, + }, pod) + if err != nil { + return corev1.PodUnknown + } + return pod.Status.Phase + }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should( + Or(Equal(corev1.PodRunning), Equal(corev1.PodSucceeded)), + "Pod should reach Running or Succeeded state") + + ginkgo.By("Verifying gather container is collecting data") + time.Sleep(30 * time.Second) // Allow time for data collection - ginkgo.By("verifying gather pod reaches Running or Completed state") - Eventually(func() corev1.PodPhase { pod := &corev1.Pod{} - err := oc.Get(ctx, gatherPod.Name, namespace, pod) - if err != nil { - return corev1.PodUnknown + err := nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: gatherPod.Name, + Namespace: testNamespace, + }, pod) + Expect(err).NotTo(HaveOccurred()) + + for _, cs := range pod.Status.ContainerStatuses { + if cs.Name == "gather" { + ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", + cs.Ready, cs.RestartCount) + + Expect(cs.RestartCount).Should(BeNumerically("<=", 2), + "Gather container should not be crash-looping") + } } - return pod.Status.Phase - }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should( - Or(Equal(corev1.PodRunning), Equal(corev1.PodSucceeded)), - "Pod should reach Running or Succeeded state") - - ginkgo.By("verifying gather container is collecting data") - // Give it time to actually collect logs - time.Sleep(60 * time.Second) - - pod := &corev1.Pod{} - err = oc.Get(ctx, gatherPod.Name, namespace, pod) - Expect(err).ShouldNot(HaveOccurred()) - - // Check gather container status - for _, cs := range pod.Status.ContainerStatuses { - if cs.Name == "gather" { - ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", - cs.Ready, cs.RestartCount) - - if cs.State.Running != nil { - ginkgo.GinkgoWriter.Println("✅ Gather container is actively collecting logs") + + ginkgo.By("Waiting for Job to complete") + Eventually(func() int32 { + _ = nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, job) + return job.Status.Succeeded + job.Status.Failed + }).WithTimeout(10*time.Minute).WithPolling(15*time.Second).Should( + BeNumerically(">", 0), "Job should eventually complete") + + ginkgo.By("Verifying MustGather CR status is updated") + fetchedMG := &mustgatherv1alpha1.MustGather{} + Eventually(func() string { + _ = nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, fetchedMG) + return fetchedMG.Status.Status + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).ShouldNot(BeEmpty(), + "MustGather status should be updated by operator") + + ginkgo.GinkgoWriter.Printf("MustGather Status: %s - Completed: %v - Reason: %s\n", + fetchedMG.Status.Status, fetchedMG.Status.Completed, fetchedMG.Status.Reason) + }) + + ginkgo.It("non-admin user should be able to monitor MustGather progress", func() { + ginkgo.By("Non-admin user creating MustGather CR") + createdMustGather = createMustGatherCR(mustGatherName, ServiceAccount) + + ginkgo.By("Non-admin user checking MustGather CR status using impersonation") + fetchedMG := &mustgatherv1alpha1.MustGather{} + Eventually(func() error { + return nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, fetchedMG) + }).WithTimeout(testTimeout).WithPolling(testInterval).Should(Succeed(), + "Non-admin user should be able to get MustGather CR") + ginkgo.GinkgoWriter.Printf("✅ Non-admin can read MustGather status: %s\n", fetchedMG.Status.Status) + + ginkgo.By("Non-admin user listing Jobs in namespace") + jobList := &batchv1.JobList{} + Eventually(func() error { + return nonAdminClient.List(testCtx, jobList, client.InNamespace(testNamespace)) + }).WithTimeout(2*time.Minute).Should(Succeed(), + "Non-admin user should be able to list Jobs") + ginkgo.GinkgoWriter.Printf("✅ Non-admin can see %d Jobs\n", len(jobList.Items)) + + ginkgo.By("Non-admin user listing Pods in namespace") + podList := &corev1.PodList{} + Eventually(func() error { + return nonAdminClient.List(testCtx, podList, client.InNamespace(testNamespace)) + }).WithTimeout(testTimeout).Should(Succeed(), + "Non-admin user should be able to list Pods") + ginkgo.GinkgoWriter.Printf("✅ Non-admin can see %d Pods\n", len(podList.Items)) + + ginkgo.By("Non-admin user checking for gather Pods") + Eventually(func() bool { + pods := &corev1.PodList{} + if err := nonAdminClient.List(testCtx, pods, + client.InNamespace(testNamespace), + client.MatchingLabels{"job-name": mustGatherName}); err != nil { + return false } + if len(pods.Items) > 0 { + ginkgo.GinkgoWriter.Printf("✅ Non-admin can see gather pod: %s\n", pods.Items[0].Name) + } + return len(pods.Items) > 0 + }).WithTimeout(3 * time.Minute).Should(BeTrue()) + + ginkgo.GinkgoWriter.Println("✅ Non-admin user successfully monitored MustGather progress") + }) + }) - if cs.State.Terminated != nil { - ginkgo.GinkgoWriter.Printf("Gather terminated - ExitCode: %d, Reason: %s\n", - cs.State.Terminated.ExitCode, cs.State.Terminated.Reason) + ginkgo.Context("Security and Isolation Tests", func() { + ginkgo.It("should fail when non-admin user tries to use privileged ServiceAccount", func() { + mustGatherName := fmt.Sprintf("test-privileged-sa-%d", time.Now().Unix()) - if cs.State.Terminated.ExitCode == 0 { - ginkgo.GinkgoWriter.Println("✅ Gather completed successfully") - } + ginkgo.By("Attempting to create MustGather with non-existent privileged SA") + mg := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: testNamespace, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: "cluster-admin-sa", // Doesn't exist + }, + } + + err := adminClient.Create(testCtx, mg) + Expect(err).NotTo(HaveOccurred(), "CR creation should succeed") + + defer func() { + _ = adminClient.Delete(testCtx, mg) + }() + + ginkgo.By("Verifying Job is created but Pod fails due to missing SA") + job := &batchv1.Job{} + Eventually(func() error { + return adminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, job) + }).WithTimeout(2 * time.Minute).Should(Succeed()) + + // Job should exist but pods won't be created due to missing SA + Consistently(func() int32 { + _ = adminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, job) + return job.Status.Active + }).WithTimeout(30*time.Second).WithPolling(5*time.Second).Should(Equal(int32(0)), + "Job should not have active pods due to missing ServiceAccount") + }) + + ginkgo.It("should enforce ServiceAccount permissions during data collection", func() { + mustGatherName := fmt.Sprintf("test-sa-permissions-%d", time.Now().Unix()) + + ginkgo.By("Creating MustGather with SA") + mg := createMustGatherCR(mustGatherName, ServiceAccount) + + defer func() { + _ = adminClient.Delete(testCtx, mg) + }() + + ginkgo.By("Waiting for Pod to start") + var gatherPod *corev1.Pod + Eventually(func() bool { + podList := &corev1.PodList{} + if err := adminClient.List(testCtx, podList, + client.InNamespace(testNamespace), + client.MatchingLabels{"job-name": mustGatherName}); err != nil { + return false + } + if len(podList.Items) > 0 { + gatherPod = &podList.Items[0] + return true } + return false + }).WithTimeout(3 * time.Minute).Should(BeTrue()) + + ginkgo.By("Verifying Pod has no privilege escalation") + Expect(gatherPod.Spec.ServiceAccountName).To(Equal(ServiceAccount)) - // Verify container didn't crash - Expect(cs.RestartCount).Should(BeNumerically("<=", 2), - "Gather container should not be crash-looping") + // Check that pod doesn't request privileged mode + for _, container := range gatherPod.Spec.Containers { + if container.SecurityContext != nil && container.SecurityContext.Privileged != nil { + Expect(*container.SecurityContext.Privileged).To(BeFalse(), + "Container should not run in privileged mode") + } } - } - ginkgo.By("checking for node-related events") - events := &corev1.EventList{} - err = oc.WithNamespace(namespace).List(ctx, events) - Expect(err).ShouldNot(HaveOccurred()) + ginkgo.By("Checking for permission denied errors in events") + time.Sleep(45 * time.Second) // Allow time for potential errors - permissionErrors := []string{} - nodeAccessErrors := []string{} - for _, event := range events.Items { - if event.InvolvedObject.Name == mustGatherName || - event.InvolvedObject.Name == gatherPod.Name { - msg := strings.ToLower(event.Message) + events := &corev1.EventList{} + err := adminClient.List(testCtx, events, client.InNamespace(testNamespace)) + Expect(err).NotTo(HaveOccurred()) - if strings.Contains(msg, "forbidden") || strings.Contains(msg, "unauthorized") { + // Log any permission errors (expected if SA doesn't have full access) + permissionErrors := []string{} + for _, event := range events.Items { + if (event.InvolvedObject.Name == mustGatherName || + event.InvolvedObject.Name == gatherPod.Name) && + (strings.Contains(strings.ToLower(event.Message), "forbidden") || + strings.Contains(strings.ToLower(event.Message), "unauthorized")) { permissionErrors = append(permissionErrors, event.Message) } + } - if strings.Contains(msg, "node") && strings.Contains(msg, "error") { - nodeAccessErrors = append(nodeAccessErrors, event.Message) + if len(permissionErrors) > 0 { + ginkgo.GinkgoWriter.Println("⚠️ Expected permission restrictions observed:") + for _, err := range permissionErrors { + ginkgo.GinkgoWriter.Printf(" - %s\n", err) } } - } + }) - if len(permissionErrors) > 0 { - ginkgo.GinkgoWriter.Println("⚠️ Permission errors detected:") - for _, err := range permissionErrors { - ginkgo.GinkgoWriter.Printf(" - %s\n", err) + ginkgo.It("should prevent non-admin user from modifying RBAC", func() { + ginkgo.By("Verifying non-admin cannot modify ClusterRole") + sar := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "update", + Group: "rbac.authorization.k8s.io", + Resource: "clusterroles", + Name: nonAdminCRRoleName, + }, + }, } - } - Expect(permissionErrors).To(BeEmpty(), "Should not have permission errors") - - ginkgo.By("waiting for must-gather Job to complete") - Eventually(func() int32 { - _ = oc.Get(ctx, mustGatherName, namespace, job) - return job.Status.Succeeded + job.Status.Failed - }).WithTimeout(15*time.Minute).WithPolling(15*time.Second).Should( - BeNumerically(">", 0), "Job should eventually complete") - - ginkgo.By("verifying Job completion status") - err = oc.Get(ctx, mustGatherName, namespace, job) - Expect(err).ShouldNot(HaveOccurred()) - ginkgo.GinkgoWriter.Printf("Job Status - Active: %d, Succeeded: %d, Failed: %d\n", - job.Status.Active, job.Status.Succeeded, job.Status.Failed) - - ginkgo.By("verifying MustGather CR status reflects completion") - createdMG := &mustgatherv1alpha1.MustGather{} - Eventually(func() bool { - _ = oc.Get(ctx, mustGatherName, namespace, createdMG) - return createdMG.Status.Status != "" - }).WithTimeout(2 * time.Minute).Should(BeTrue()) - - ginkgo.By("checking final status of node log collection") - Expect(createdMG.Status.Status).Should(Or(Equal("Completed"), Equal("Failed"))) - ginkgo.GinkgoWriter.Printf("MustGather Status: %s - %s\n", - createdMG.Status.Status, createdMG.Status.Reason) - - if createdMG.Status.Status == "Completed" { - Expect(createdMG.Status.Completed).To(BeTrue()) - ginkgo.GinkgoWriter.Printf("✅ Successfully collected node logs from %d nodes\n", nodeCount) - ginkgo.GinkgoWriter.Println(" Node logs include: kubelet, container runtime, system logs (journalctl)") - } else { - ginkgo.GinkgoWriter.Printf("⚠️ Node log collection failed: %s\n", createdMG.Status.Reason) - } - }) - - ginkgo.It("can collect CRD definitions and custom resource instances", func(ctx context.Context) { - mustGatherName := "test-crd-collection" + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeFalse(), + "Non-admin should NOT be able to modify ClusterRoles") - ginkgo.By("ensuring clean state before test") - _ = oc.Delete(ctx, &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, - }) - _ = oc.Delete(ctx, &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, - }) - Eventually(func() bool { - err := oc.Get(ctx, mustGatherName, namespace, &mustgatherv1alpha1.MustGather{}) - return errors.IsNotFound(err) - }).WithTimeout(60 * time.Second).Should(BeTrue()) - - ginkgo.By("verifying CRDs exist in the cluster") - crdList := &apiextensionsv1.CustomResourceDefinitionList{} - err := oc.List(ctx, crdList) - Expect(err).ShouldNot(HaveOccurred(), "should be able to list CRDs") - Expect(len(crdList.Items)).Should(BeNumerically(">", 0), "cluster should have CRDs") - - ginkgo.GinkgoWriter.Printf("Found %d CRDs in cluster\n", len(crdList.Items)) - - // Find the MustGather CRD as an example - var mustGatherCRD *apiextensionsv1.CustomResourceDefinition - for i := range crdList.Items { - if crdList.Items[i].Name == "mustgathers.operator.openshift.io" { - mustGatherCRD = &crdList.Items[i] - ginkgo.GinkgoWriter.Printf("Found MustGather CRD: %s (group: %s, version: %s)\n", - mustGatherCRD.Name, - mustGatherCRD.Spec.Group, - mustGatherCRD.Spec.Versions[0].Name) - break + ginkgo.By("Verifying non-admin cannot modify ServiceAccount") + sar2 := &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + User: nonAdminUser, + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Verb: "update", + Group: "", + Resource: "serviceaccounts", + Name: ServiceAccount, + Namespace: testNamespace, + }, + }, } - } - Expect(mustGatherCRD).ToNot(BeNil(), "MustGather CRD should exist") + err = adminClient.Create(testCtx, sar2) + Expect(err).NotTo(HaveOccurred()) + Expect(sar2.Status.Allowed).To(BeFalse(), + "Non-admin should NOT be able to modify ServiceAccounts") + }) + }) - ginkgo.By("verifying must-gather-admin SA can access CRDs and custom resources") - checkSAPermission := func(verb, group, resource string) { + ginkgo.Context("Cleanup and Artifact Retrieval", func() { + ginkgo.It("non-admin user should be able to delete their MustGather CR (SAR check)", func() { + mustGatherName := fmt.Sprintf("test-cleanup-%d", time.Now().Unix()) + + ginkgo.By("Verifying non-admin can delete MustGather CRs") sar := &authorizationv1.SubjectAccessReview{ Spec: authorizationv1.SubjectAccessReviewSpec{ - User: fmt.Sprintf("system:serviceaccount:%s:must-gather-admin", namespace), + User: nonAdminUser, ResourceAttributes: &authorizationv1.ResourceAttributes{ - Verb: verb, - Group: group, - Resource: resource, + Verb: "delete", + Group: "operator.openshift.io", + Resource: "mustgathers", + Name: mustGatherName, + Namespace: testNamespace, }, }, } - err := oc.Create(ctx, sar) - Expect(err).ShouldNot(HaveOccurred()) - Expect(sar.Status.Allowed).To(BeTrue(), - "SA must be able to %s %s.%s for CRD collection", verb, resource, group) - ginkgo.GinkgoWriter.Printf(" ✓ SA can %s %s.%s\n", verb, resource, group) - } + err := adminClient.Create(testCtx, sar) + Expect(err).NotTo(HaveOccurred()) + Expect(sar.Status.Allowed).To(BeTrue(), "Non-admin should be able to delete MustGather CR") + }) + + ginkgo.It("non-admin user can actually delete their MustGather CR using impersonation", func() { + mustGatherName := fmt.Sprintf("test-cleanup-impersonate-%d", time.Now().Unix()) - // Check permissions for CRD access - checkSAPermission("list", "apiextensions.k8s.io", "customresourcedefinitions") - checkSAPermission("get", "apiextensions.k8s.io", "customresourcedefinitions") - - // Check permissions for MustGather custom resources - checkSAPermission("list", "operator.openshift.io", "mustgathers") - checkSAPermission("get", "operator.openshift.io", "mustgathers") - - ginkgo.By("verifying custom resource instances exist") - mustGatherList := &mustgatherv1alpha1.MustGatherList{} - err = oc.List(ctx, mustGatherList) - Expect(err).ShouldNot(HaveOccurred(), "should be able to list MustGather CRs") - ginkgo.GinkgoWriter.Printf("Found %d MustGather CR instances in cluster\n", len(mustGatherList.Items)) - - ginkgo.By("creating a test MustGather CR instance for collection") - testCR := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-cr-instance", - Namespace: namespace, - Labels: map[string]string{ - "test": "crd-collection", - "app": "must-gather-operator-e2e", + ginkgo.By("Non-admin user creating MustGather CR") + mg := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: mustGatherName, + Namespace: testNamespace, }, - Annotations: map[string]string{ - "description": "Test CR for e2e CRD collection validation", + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: ServiceAccount, }, + } + err := nonAdminClient.Create(testCtx, mg) + Expect(err).NotTo(HaveOccurred()) + + ginkgo.By("Non-admin user deleting their MustGather CR") + err = nonAdminClient.Delete(testCtx, mg) + Expect(err).NotTo(HaveOccurred(), "Non-admin should be able to delete their own CR") + + ginkgo.By("Verifying CR is deleted") + Eventually(func() bool { + err := nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, &mustgatherv1alpha1.MustGather{}) + return apierrors.IsNotFound(err) + }).WithTimeout(60 * time.Second).Should(BeTrue()) + + ginkgo.GinkgoWriter.Printf("✅ Non-admin user successfully deleted MustGather CR: %s\n", mustGatherName) + }) + + ginkgo.It("should clean up Job and Pod when MustGather CR is deleted", func() { + mustGatherName := fmt.Sprintf("test-cascading-delete-%d", time.Now().Unix()) + + ginkgo.By("Creating MustGather CR") + mg := createMustGatherCR(mustGatherName, ServiceAccount) + + ginkgo.By("Waiting for Job to be created") + Eventually(func() error { + return adminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, &batchv1.Job{}) + }).WithTimeout(2 * time.Minute).Should(Succeed()) + + ginkgo.By("Deleting MustGather CR") + err := adminClient.Delete(testCtx, mg) + Expect(err).NotTo(HaveOccurred()) + + ginkgo.By("Verifying Job is eventually cleaned up") + Eventually(func() bool { + err := adminClient.Get(testCtx, client.ObjectKey{ + Name: mustGatherName, + Namespace: testNamespace, + }, &batchv1.Job{}) + return apierrors.IsNotFound(err) + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), + "Job should be cleaned up when MustGather CR is deleted") + }) + }) +}) + +// Helper Functions + +func createNonAdminClient() client.Client { + ginkgo.By("Creating impersonated non-admin client") + nonAdminConfig := rest.CopyConfig(adminRestConfig) + nonAdminConfig.Impersonate = rest.ImpersonationConfig{ + UserName: nonAdminUser, + } + + c, err := client.New(nonAdminConfig, client.Options{Scheme: testScheme}) + Expect(err).NotTo(HaveOccurred(), "Failed to create non-admin impersonation client") + + ginkgo.GinkgoWriter.Printf("✅ Created impersonated client for user: %s\n", nonAdminUser) + return c +} + +func verifyOperatorDeployment() { + ginkgo.By("Checking must-gather-operator namespace exists") + ns := &corev1.Namespace{} + err := adminClient.Get(testCtx, client.ObjectKey{Name: "must-gather-operator"}, ns) + Expect(err).NotTo(HaveOccurred(), "must-gather-operator namespace should exist") + + ginkgo.By("Checking must-gather-operator deployment exists and is available") + deployment := &appsv1.Deployment{} + err = adminClient.Get(testCtx, client.ObjectKey{ + Name: "must-gather-operator", + Namespace: "must-gather-operator", + }, deployment) + Expect(err).NotTo(HaveOccurred(), "must-gather-operator deployment should exist") + + // Verify deployment has at least one ready replica + Expect(deployment.Status.ReadyReplicas).Should(BeNumerically(">", 0), + "must-gather-operator deployment should have at least one ready replica") + + ginkgo.GinkgoWriter.Printf("✅ must-gather-operator is deployed and available (ready replicas: %d)\n", + deployment.Status.ReadyReplicas) +} + +func createTestNamespace() { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespace, + Labels: map[string]string{ + "test": "non-admin-must-gather", }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "must-gather-admin", - }, - } + }, + } - err = oc.Create(ctx, testCR) - Expect(err).ShouldNot(HaveOccurred(), "should be able to create test CR") - - // Cleanup test CR after test - defer func() { - _ = oc.Delete(ctx, testCR) - }() - - ginkgo.By("verifying the test CR was created successfully") - createdCR := &mustgatherv1alpha1.MustGather{} - err = oc.Get(ctx, "test-cr-instance", namespace, createdCR) - Expect(err).ShouldNot(HaveOccurred()) - Expect(createdCR.Labels["test"]).To(Equal("crd-collection")) - ginkgo.GinkgoWriter.Printf("✅ Test CR created: %s with labels: %v\n", - createdCR.Name, createdCR.Labels) - - ginkgo.By("creating MustGather to collect CRDs and custom resources") - mustgather := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - Name: mustGatherName, - Namespace: namespace, + err := adminClient.Create(testCtx, ns) + if !apierrors.IsAlreadyExists(err) { + Expect(err).NotTo(HaveOccurred(), "Failed to create test namespace") + } + + ginkgo.GinkgoWriter.Printf("✅ Created test namespace: %s\n", testNamespace) +} + +func createNonAdminClusterRole() { + cr := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: nonAdminCRRoleName, + Labels: map[string]string{ + "test": "non-admin-must-gather", }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "must-gather-admin", - RetainResourcesOnCompletion: boolPtr(true), + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"operator.openshift.io"}, + Resources: []string{"mustgathers"}, + Verbs: []string{"*"}, }, - } + { + APIGroups: []string{"operator.openshift.io"}, + Resources: []string{"mustgathers/status"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"batch"}, + Resources: []string{"jobs"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"pods/log"}, + Verbs: []string{"get", "list"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"events"}, + Verbs: []string{"get", "list"}, + }, + }, + } - err = oc.Create(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather CR") - - // Ensure cleanup - ginkgo.DeferCleanup(func(ctx context.Context) { - ginkgo.GinkgoWriter.Println("Cleaning up CRD collection test") - _ = oc.Delete(ctx, mustgather) - _ = oc.Delete(ctx, &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{Name: mustGatherName, Namespace: namespace}, - }) - }, ctx) - - ginkgo.By("verifying Job is created for CRD collection") - job := &batchv1.Job{} - Eventually(func() error { - return oc.Get(ctx, mustGatherName, namespace, job) - }).WithTimeout(2 * time.Minute).WithPolling(3 * time.Second).Should(Succeed()) - - ginkgo.By("verifying Job uses service account with CRD access") - Expect(job.Spec.Template.Spec.ServiceAccountName).To(Equal("must-gather-admin"), - "Job must use SA with permissions to access CRDs and custom resources") - - ginkgo.By("waiting for gather pod to be created") - var gatherPod *corev1.Pod - Eventually(func() bool { - podList := &corev1.PodList{} - if err := oc.WithNamespace(namespace).List(ctx, podList); err != nil { - return false - } - for i := range podList.Items { - if podList.Items[i].Labels["job-name"] == mustGatherName { - gatherPod = &podList.Items[i] - return true - } - } - return false - }).WithTimeout(3 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) + err := adminClient.Create(testCtx, cr) + if apierrors.IsAlreadyExists(err) { + // Update if already exists + _ = adminClient.Update(testCtx, cr) + } else { + Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRole for non-admin") + } - ginkgo.By("verifying gather pod starts collecting data") - Eventually(func() corev1.PodPhase { - pod := &corev1.Pod{} - err := oc.Get(ctx, gatherPod.Name, namespace, pod) - if err != nil { - return corev1.PodUnknown - } - return pod.Status.Phase - }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should( - Or(Equal(corev1.PodRunning), Equal(corev1.PodSucceeded)), - "Pod should reach Running or Succeeded state") - - ginkgo.By("verifying must-gather can list CRDs during collection") - // This is verified by checking that the pod started successfully - // If SA lacked CRD permissions, collection would fail - pod := &corev1.Pod{} - err = oc.Get(ctx, gatherPod.Name, namespace, pod) - Expect(err).ShouldNot(HaveOccurred()) - - for _, cs := range pod.Status.ContainerStatuses { - if cs.Name == "gather" { - ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", - cs.Ready, cs.RestartCount) - - // Verify container is not failing due to permission issues - Expect(cs.RestartCount).Should(BeNumerically("<=", 2), - "Gather should not be crash-looping (would indicate permission issues)") - - if cs.State.Waiting != nil && strings.Contains( - strings.ToLower(cs.State.Waiting.Reason), "crashloopbackoff") { - ginkgo.Fail("Gather container in CrashLoopBackOff - likely permission issue with CRD access") - } - } - } + ginkgo.GinkgoWriter.Printf("✅ Created ClusterRole: %s\n", nonAdminCRRoleName) +} - ginkgo.By("checking for CRD-related permission errors in events") - events := &corev1.EventList{} - err = oc.WithNamespace(namespace).List(ctx, events) - Expect(err).ShouldNot(HaveOccurred()) - - crdPermissionErrors := []string{} - for _, event := range events.Items { - if event.InvolvedObject.Name == mustGatherName || - event.InvolvedObject.Name == gatherPod.Name { - msg := strings.ToLower(event.Message) - - if (strings.Contains(msg, "customresourcedefinition") || - strings.Contains(msg, "crd")) && - (strings.Contains(msg, "forbidden") || strings.Contains(msg, "unauthorized")) { - crdPermissionErrors = append(crdPermissionErrors, event.Message) - } - } - } +func createNonAdminClusterRoleBinding() { + crb := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: nonAdminCRBindName, + Labels: map[string]string{ + "test": "non-admin-must-gather", + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: nonAdminCRRoleName, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "User", + Name: nonAdminUser, + APIGroup: "rbac.authorization.k8s.io", + }, + }, + } - if len(crdPermissionErrors) > 0 { - ginkgo.GinkgoWriter.Println("⚠️ CRD permission errors detected:") - for _, err := range crdPermissionErrors { - ginkgo.GinkgoWriter.Printf(" - %s\n", err) - } - } - Expect(crdPermissionErrors).To(BeEmpty(), "Should not have CRD permission errors") - - ginkgo.By("waiting for must-gather to complete") - Eventually(func() int32 { - _ = oc.Get(ctx, mustGatherName, namespace, job) - return job.Status.Succeeded + job.Status.Failed - }).WithTimeout(15*time.Minute).WithPolling(15*time.Second).Should( - BeNumerically(">", 0), "Job should eventually complete") - - ginkgo.By("verifying Job completion status") - err = oc.Get(ctx, mustGatherName, namespace, job) - Expect(err).ShouldNot(HaveOccurred()) - ginkgo.GinkgoWriter.Printf("Job Status - Active: %d, Succeeded: %d, Failed: %d\n", - job.Status.Active, job.Status.Succeeded, job.Status.Failed) - - ginkgo.By("verifying MustGather CR status reflects completion") - completedMG := &mustgatherv1alpha1.MustGather{} - Eventually(func() bool { - _ = oc.Get(ctx, mustGatherName, namespace, completedMG) - return completedMG.Status.Status != "" - }).WithTimeout(2 * time.Minute).Should(BeTrue()) - - ginkgo.By("checking final status of CRD collection") - Expect(completedMG.Status.Status).Should(Or(Equal("Completed"), Equal("Failed"))) - ginkgo.GinkgoWriter.Printf("MustGather Status: %s - %s\n", - completedMG.Status.Status, completedMG.Status.Reason) - - if completedMG.Status.Status == "Completed" { - Expect(completedMG.Status.Completed).To(BeTrue()) - ginkgo.GinkgoWriter.Println("✅ Successfully collected CRD definitions and custom resource instances") - ginkgo.GinkgoWriter.Printf(" CRDs collected: %d CRDs in cluster\n", len(crdList.Items)) - ginkgo.GinkgoWriter.Printf(" Custom resources collected: MustGather CRs and other custom resources\n") - ginkgo.GinkgoWriter.Println(" This includes CRD schemas, CR instances, and their status/spec") - } else { - ginkgo.GinkgoWriter.Printf("⚠️ CRD collection failed: %s\n", completedMG.Status.Reason) - } + err := adminClient.Create(testCtx, crb) + if apierrors.IsAlreadyExists(err) { + _ = adminClient.Update(testCtx, crb) + } else { + Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding") + } - ginkgo.By("verifying test CR still exists after collection") - err = oc.Get(ctx, "test-cr-instance", namespace, createdCR) - Expect(err).ShouldNot(HaveOccurred(), "test CR should still exist after must-gather collection") - ginkgo.GinkgoWriter.Println("✅ CRD collection is non-destructive - test CR preserved") - }) + ginkgo.GinkgoWriter.Printf("✅ Created ClusterRoleBinding: %s for user %s\n", + nonAdminCRBindName, nonAdminUser) +} - /*ginkgo.DescribeTable("MustGather can be created", func(ctx context.Context, user, group string) { - client, err := oc.Impersonate(user, group) - Expect(err).ShouldNot(HaveOccurred(), "unable to impersonate %s user in %s group", user, group) +func createServiceAccount() { + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: ServiceAccount, + Namespace: testNamespace, + Labels: map[string]string{ + "test": "non-admin-must-gather", + "type": "must-gather-sa", + }, + }, + } + + err := adminClient.Create(testCtx, sa) + if !apierrors.IsAlreadyExists(err) { + Expect(err).NotTo(HaveOccurred(), "Failed to create ServiceAccount") + } + + ginkgo.GinkgoWriter.Printf("✅ Created ServiceAccount: %s/%s\n", + testNamespace, ServiceAccount) +} - mustgather := &mustgatherv1alpha1.MustGather{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "osde2e-", - Namespace: namespace, +func createSARBACForMustGather() { + // Create ClusterRole with minimal read permissions + cr := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: SARoleName, + Labels: map[string]string{ + "test": "non-admin-must-gather", }, - Spec: mustgatherv1alpha1.MustGatherSpec{ - ServiceAccountName: "must-gather-admin", - UploadTarget: &mustgatherv1alpha1.UploadTargetSpec{ - Type: mustgatherv1alpha1.UploadTypeSFTP, - SFTP: &mustgatherv1alpha1.SFTPSpec{ - CaseID: "0000000", - CaseManagementAccountSecretRef: corev1.LocalObjectReference{ - Name: "case-management-creds", - }, - }, - }, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"nodes", "pods", "namespaces", "services", + "configmaps", "events", "persistentvolumes", "persistentvolumeclaims"}, + Verbs: []string{"get", "list"}, }, - } + { + APIGroups: []string{"apps"}, + Resources: []string{"deployments", "daemonsets", "replicasets", "statefulsets"}, + Verbs: []string{"get", "list"}, + }, + { + APIGroups: []string{"batch"}, + Resources: []string{"jobs", "cronjobs"}, + Verbs: []string{"get", "list"}, + }, + // Add more resources as needed for must-gather + }, + } - err = client.Create(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to create mustgather as %s", group) + err := adminClient.Create(testCtx, cr) + if apierrors.IsAlreadyExists(err) { + _ = adminClient.Update(testCtx, cr) + } else { + Expect(err).NotTo(HaveOccurred(), "Failed to create SA ClusterRole") + } - // TODO: do something with them? + // Create ClusterRoleBinding + crb := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: SARoleBindName, + Labels: map[string]string{ + "test": "non-admin-must-gather", + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: SARoleName, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: ServiceAccount, + Namespace: testNamespace, + }, + }, + } - err = client.Delete(ctx, mustgather) - Expect(err).ShouldNot(HaveOccurred(), "failed to delete mustgather as %s", group) - }, - ginkgo.Entry("by backplane-cluster-admin", "backplane-cluster-admin", ""), - ginkgo.Entry("by openshift-backplane-cee", "test@redhat.com", "system:serviceaccounts:openshift-backplane-cee"), - )*/ + err = adminClient.Create(testCtx, crb) + if apierrors.IsAlreadyExists(err) { + _ = adminClient.Update(testCtx, crb) + } else { + Expect(err).NotTo(HaveOccurred(), "Failed to create SA ClusterRoleBinding") + } - /*ginkgo.PIt("can be upgraded", func(ctx context.Context) { - ginkgo.By("forcing operator upgrade") - err := oc.UpgradeOperator(ctx, operator, namespace) - Expect(err).NotTo(HaveOccurred(), "operator upgrade failed") - })*/ -}) + ginkgo.GinkgoWriter.Printf("✅ Created RBAC for SA: %s\n", SARoleName) +} -// Helper function to extract node roles from labels -func getNodeRoles(node corev1.Node) string { - roles := []string{} - for label := range node.Labels { - if strings.HasPrefix(label, "node-role.kubernetes.io/") { - role := strings.TrimPrefix(label, "node-role.kubernetes.io/") - if role != "" { - roles = append(roles, role) - } - } +func createMustGatherCR(name, serviceAccountName string) *mustgatherv1alpha1.MustGather { + mg := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Labels: map[string]string{ + "test": "non-admin-must-gather", + }, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: serviceAccountName, + }, } - if len(roles) == 0 { - return "none" + + err := nonAdminClient.Create(testCtx, mg) + Expect(err).NotTo(HaveOccurred(), "Failed to create MustGather CR") + + return mg +} + +func createMustGatherCRWithRetention(name, serviceAccountName string) *mustgatherv1alpha1.MustGather { + retainResources := true + mg := &mustgatherv1alpha1.MustGather{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Labels: map[string]string{ + "test": "non-admin-must-gather", + }, + }, + Spec: mustgatherv1alpha1.MustGatherSpec{ + ServiceAccountName: serviceAccountName, + RetainResourcesOnCompletion: &retainResources, + }, } - return strings.Join(roles, ", ") + + err := nonAdminClient.Create(testCtx, mg) + Expect(err).NotTo(HaveOccurred(), "Failed to create MustGather CR with retention") + + return mg } -// Helper function to convert bool to pointer -func boolPtr(b bool) *bool { - return &b +func cleanupTestResources() { + policy := metav1.DeletePropagationForeground + deleteOpts := &client.DeleteOptions{PropagationPolicy: &policy} + + // Delete namespace (will delete SA and MustGather CRs) + ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}} + _ = adminClient.Delete(testCtx, ns, deleteOpts) + + // Delete ClusterRoleBindings + crb1 := &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: nonAdminCRBindName}} + _ = adminClient.Delete(testCtx, crb1, deleteOpts) + + crb2 := &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: SARoleBindName}} + _ = adminClient.Delete(testCtx, crb2, deleteOpts) + + // Delete ClusterRoles + cr1 := &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: nonAdminCRRoleName}} + _ = adminClient.Delete(testCtx, cr1, deleteOpts) + + cr2 := &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: SARoleName}} + _ = adminClient.Delete(testCtx, cr2, deleteOpts) + + ginkgo.GinkgoWriter.Println("✅ Cleanup complete") } From 743687d008462255d2674be3eae461f6ecaea1e1 Mon Sep 17 00:00:00 2001 From: Praveen Jaya Kumar Date: Tue, 18 Nov 2025 22:00:30 +0530 Subject: [PATCH 03/10] Fix the inconsistencies --- test/e2e/must_gather_operator_tests.go | 127 ++++++++++++++----------- 1 file changed, 72 insertions(+), 55 deletions(-) diff --git a/test/e2e/must_gather_operator_tests.go b/test/e2e/must_gather_operator_tests.go index 448852bb2..72dc7bf9a 100644 --- a/test/e2e/must_gather_operator_tests.go +++ b/test/e2e/must_gather_operator_tests.go @@ -36,8 +36,6 @@ const ( ServiceAccount = "must-gather-sa" SARoleName = "sa-reader-role" SARoleBindName = "sa-reader-binding" - testTimeout = time.Second * 30 - testInterval = time.Millisecond * 250 ) // Test suite variables @@ -157,13 +155,13 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Expect(hasNonAdminUser).To(BeTrue(), "ClusterRoleBinding should include non-admin user") }) - ginkgo.It("should have created ServiceAccount", func() { + ginkgo.It("should have created ServiceAccount", func() { sa := &corev1.ServiceAccount{} err := adminClient.Get(testCtx, client.ObjectKey{ Name: ServiceAccount, Namespace: testNamespace, }, sa) - Expect(err).NotTo(HaveOccurred(), " ServiceAccount should exist") + Expect(err).NotTo(HaveOccurred(), "ServiceAccount should exist") }) ginkgo.It("should have granted minimal read permissions to ServiceAccount", func() { @@ -397,7 +395,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() err := adminClient.Create(testCtx, sar) Expect(err).NotTo(HaveOccurred()) Expect(sar.Status.Allowed).To(BeTrue(), - fmt.Sprintf(" SA should be able to %s %s.%s", verb, resource, group)) + fmt.Sprintf("SA should be able to %s %s.%s", verb, resource, group)) } // Check read permissions for must-gather required resources @@ -426,7 +424,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() err := adminClient.Create(testCtx, sar) Expect(err).NotTo(HaveOccurred()) Expect(sar.Status.Allowed).To(BeFalse(), - " SA should NOT be able to delete pods") + "SA should NOT be able to delete pods") // Verify SA cannot create secrets sar2 := &authorizationv1.SubjectAccessReview{ @@ -442,7 +440,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() err = adminClient.Create(testCtx, sar2) Expect(err).NotTo(HaveOccurred()) Expect(sar2.Status.Allowed).To(BeFalse(), - " SA should NOT be able to create secrets") + "SA should NOT be able to create secrets") }) }) @@ -466,7 +464,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Namespace: testNamespace, }, &mustgatherv1alpha1.MustGather{}) return apierrors.IsNotFound(err) - }).WithTimeout(60 * time.Second).WithPolling(2 * time.Second).Should(BeTrue()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) } }) @@ -529,7 +527,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() } } return err - }).WithTimeout(2*time.Minute).WithPolling(3*time.Second).Should(Succeed(), + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(Succeed(), "Non-admin user should be able to see Job created by operator") ginkgo.By("Verifying Job uses ServiceAccount") @@ -572,7 +570,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Name: mustGatherName, Namespace: testNamespace, }, job) - }).WithTimeout(2 * time.Minute).WithPolling(3 * time.Second).Should(Succeed()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) ginkgo.By("Waiting for Pod to be created") var gatherPod *corev1.Pod @@ -588,7 +586,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() return true } return false - }).WithTimeout(3*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), "Pod should be created by Job") ginkgo.By("Verifying Pod uses ServiceAccount") @@ -617,29 +615,39 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() return corev1.PodUnknown } return pod.Status.Phase - }).WithTimeout(5*time.Minute).WithPolling(10*time.Second).Should( + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should( Or(Equal(corev1.PodRunning), Equal(corev1.PodSucceeded)), "Pod should reach Running or Succeeded state") ginkgo.By("Verifying gather container is collecting data") - time.Sleep(30 * time.Second) // Allow time for data collection - - pod := &corev1.Pod{} - err := nonAdminClient.Get(testCtx, client.ObjectKey{ - Name: gatherPod.Name, - Namespace: testNamespace, - }, pod) - Expect(err).NotTo(HaveOccurred()) + Eventually(func() bool { + pod := &corev1.Pod{} + err := nonAdminClient.Get(testCtx, client.ObjectKey{ + Name: gatherPod.Name, + Namespace: testNamespace, + }, pod) + if err != nil { + return false + } - for _, cs := range pod.Status.ContainerStatuses { - if cs.Name == "gather" { - ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", - cs.Ready, cs.RestartCount) + for _, cs := range pod.Status.ContainerStatuses { + if cs.Name == "gather" { + ginkgo.GinkgoWriter.Printf("Gather container - Ready: %v, RestartCount: %d\n", + cs.Ready, cs.RestartCount) - Expect(cs.RestartCount).Should(BeNumerically("<=", 2), - "Gather container should not be crash-looping") + // Container should be running and not crash-looping + if cs.RestartCount > 2 { + return false + } + // Return true if container has started (even if not ready yet, data collection may be in progress) + if cs.State.Running != nil || cs.State.Terminated != nil { + return true + } + } } - } + return false + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), + "Gather container should be running and collecting data without excessive restarts") ginkgo.By("Waiting for Job to complete") Eventually(func() int32 { @@ -648,7 +656,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Namespace: testNamespace, }, job) return job.Status.Succeeded + job.Status.Failed - }).WithTimeout(10*time.Minute).WithPolling(15*time.Second).Should( + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should( BeNumerically(">", 0), "Job should eventually complete") ginkgo.By("Verifying MustGather CR status is updated") @@ -677,7 +685,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Name: mustGatherName, Namespace: testNamespace, }, fetchedMG) - }).WithTimeout(testTimeout).WithPolling(testInterval).Should(Succeed(), + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(Succeed(), "Non-admin user should be able to get MustGather CR") ginkgo.GinkgoWriter.Printf("✅ Non-admin can read MustGather status: %s\n", fetchedMG.Status.Status) @@ -685,7 +693,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() jobList := &batchv1.JobList{} Eventually(func() error { return nonAdminClient.List(testCtx, jobList, client.InNamespace(testNamespace)) - }).WithTimeout(2*time.Minute).Should(Succeed(), + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(Succeed(), "Non-admin user should be able to list Jobs") ginkgo.GinkgoWriter.Printf("✅ Non-admin can see %d Jobs\n", len(jobList.Items)) @@ -693,7 +701,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() podList := &corev1.PodList{} Eventually(func() error { return nonAdminClient.List(testCtx, podList, client.InNamespace(testNamespace)) - }).WithTimeout(testTimeout).Should(Succeed(), + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(Succeed(), "Non-admin user should be able to list Pods") ginkgo.GinkgoWriter.Printf("✅ Non-admin can see %d Pods\n", len(podList.Items)) @@ -709,7 +717,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() ginkgo.GinkgoWriter.Printf("✅ Non-admin can see gather pod: %s\n", pods.Items[0].Name) } return len(pods.Items) > 0 - }).WithTimeout(3 * time.Minute).Should(BeTrue()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) ginkgo.GinkgoWriter.Println("✅ Non-admin user successfully monitored MustGather progress") }) @@ -730,11 +738,11 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() }, } - err := adminClient.Create(testCtx, mg) + err := nonAdminClient.Create(testCtx, mg) Expect(err).NotTo(HaveOccurred(), "CR creation should succeed") defer func() { - _ = adminClient.Delete(testCtx, mg) + _ = nonAdminClient.Delete(testCtx, mg) }() ginkgo.By("Verifying Job is created but Pod fails due to missing SA") @@ -744,7 +752,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Name: mustGatherName, Namespace: testNamespace, }, job) - }).WithTimeout(2 * time.Minute).Should(Succeed()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) // Job should exist but pods won't be created due to missing SA Consistently(func() int32 { @@ -753,18 +761,18 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Namespace: testNamespace, }, job) return job.Status.Active - }).WithTimeout(30*time.Second).WithPolling(5*time.Second).Should(Equal(int32(0)), + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(Equal(int32(0)), "Job should not have active pods due to missing ServiceAccount") }) ginkgo.It("should enforce ServiceAccount permissions during data collection", func() { mustGatherName := fmt.Sprintf("test-sa-permissions-%d", time.Now().Unix()) - ginkgo.By("Creating MustGather with SA") + ginkgo.By("Creating MustGather with SA") mg := createMustGatherCR(mustGatherName, ServiceAccount) defer func() { - _ = adminClient.Delete(testCtx, mg) + _ = nonAdminClient.Delete(testCtx, mg) }() ginkgo.By("Waiting for Pod to start") @@ -781,7 +789,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() return true } return false - }).WithTimeout(3 * time.Minute).Should(BeTrue()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) ginkgo.By("Verifying Pod has no privilege escalation") Expect(gatherPod.Spec.ServiceAccountName).To(Equal(ServiceAccount)) @@ -795,22 +803,31 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() } ginkgo.By("Checking for permission denied errors in events") - time.Sleep(45 * time.Second) // Allow time for potential errors + var permissionErrors []string - events := &corev1.EventList{} - err := adminClient.List(testCtx, events, client.InNamespace(testNamespace)) - Expect(err).NotTo(HaveOccurred()) + // Wait for events to be populated (if any permission errors occur, they should appear) + Eventually(func() bool { + events := &corev1.EventList{} + err := adminClient.List(testCtx, events, client.InNamespace(testNamespace)) + if err != nil { + return false + } - // Log any permission errors (expected if SA doesn't have full access) - permissionErrors := []string{} - for _, event := range events.Items { - if (event.InvolvedObject.Name == mustGatherName || - event.InvolvedObject.Name == gatherPod.Name) && - (strings.Contains(strings.ToLower(event.Message), "forbidden") || - strings.Contains(strings.ToLower(event.Message), "unauthorized")) { - permissionErrors = append(permissionErrors, event.Message) + // Collect any permission errors + permissionErrors = []string{} + for _, event := range events.Items { + if (event.InvolvedObject.Name == mustGatherName || + event.InvolvedObject.Name == gatherPod.Name) && + (strings.Contains(strings.ToLower(event.Message), "forbidden") || + strings.Contains(strings.ToLower(event.Message), "unauthorized")) { + permissionErrors = append(permissionErrors, event.Message) + } } - } + + // Return true once we have events (or after checking for a reasonable time) + return len(events.Items) > 0 + }).WithTimeout(2*time.Minute).WithPolling(5*time.Second).Should(BeTrue(), + "Events should be available for inspection") if len(permissionErrors) > 0 { ginkgo.GinkgoWriter.Println("⚠️ Expected permission restrictions observed:") @@ -907,7 +924,7 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Namespace: testNamespace, }, &mustgatherv1alpha1.MustGather{}) return apierrors.IsNotFound(err) - }).WithTimeout(60 * time.Second).Should(BeTrue()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue()) ginkgo.GinkgoWriter.Printf("✅ Non-admin user successfully deleted MustGather CR: %s\n", mustGatherName) }) @@ -924,10 +941,10 @@ var _ = ginkgo.Describe("Must-Gather Operator E2E Tests", ginkgo.Ordered, func() Name: mustGatherName, Namespace: testNamespace, }, &batchv1.Job{}) - }).WithTimeout(2 * time.Minute).Should(Succeed()) + }).WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) ginkgo.By("Deleting MustGather CR") - err := adminClient.Delete(testCtx, mg) + err := nonAdminClient.Delete(testCtx, mg) Expect(err).NotTo(HaveOccurred()) ginkgo.By("Verifying Job is eventually cleaned up") From a1060858a758f19b6e5586779047c4cac5e06943 Mon Sep 17 00:00:00 2001 From: Praveen Jaya Kumar Date: Tue, 18 Nov 2025 22:52:00 +0530 Subject: [PATCH 04/10] fix dependency issue --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index dae2733d6..e9023f774 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/prometheus/client_golang v1.22.0 github.com/redhat-cop/operator-utils v1.3.7 k8s.io/api v0.33.3 - k8s.io/apiextensions-apiserver v0.33.0 k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.3 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff @@ -89,6 +88,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.33.0 // indirect k8s.io/cli-runtime v0.28.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kubectl v0.28.2 // indirect From 3a7c0b4d041bcc4d6e2ae5b653c80f66f26e921d Mon Sep 17 00:00:00 2001 From: Praveen Jaya Kumar Date: Tue, 18 Nov 2025 23:29:39 +0530 Subject: [PATCH 05/10] fix the dependency issue --- go.mod | 5 - go.sum | 14 - .../github.com/gorilla/websocket/.gitignore | 25 - vendor/github.com/gorilla/websocket/AUTHORS | 9 - vendor/github.com/gorilla/websocket/LICENSE | 22 - vendor/github.com/gorilla/websocket/README.md | 32 - vendor/github.com/gorilla/websocket/client.go | 517 - .../gorilla/websocket/compression.go | 152 - vendor/github.com/gorilla/websocket/conn.go | 1246 -- vendor/github.com/gorilla/websocket/doc.go | 227 - vendor/github.com/gorilla/websocket/join.go | 42 - vendor/github.com/gorilla/websocket/json.go | 60 - vendor/github.com/gorilla/websocket/mask.go | 55 - .../github.com/gorilla/websocket/mask_safe.go | 16 - .../github.com/gorilla/websocket/prepared.go | 102 - vendor/github.com/gorilla/websocket/proxy.go | 104 - vendor/github.com/gorilla/websocket/server.go | 373 - vendor/github.com/gorilla/websocket/util.go | 298 - .../moby/spdystream/CONTRIBUTING.md | 13 - vendor/github.com/moby/spdystream/LICENSE | 202 - vendor/github.com/moby/spdystream/MAINTAINERS | 40 - vendor/github.com/moby/spdystream/NOTICE | 5 - vendor/github.com/moby/spdystream/README.md | 77 - .../github.com/moby/spdystream/connection.go | 991 - vendor/github.com/moby/spdystream/handlers.go | 52 - vendor/github.com/moby/spdystream/priority.go | 114 - .../moby/spdystream/spdy/dictionary.go | 203 - .../github.com/moby/spdystream/spdy/read.go | 364 - .../github.com/moby/spdystream/spdy/types.go | 291 - .../github.com/moby/spdystream/spdy/write.go | 334 - vendor/github.com/moby/spdystream/stream.go | 345 - vendor/github.com/moby/spdystream/utils.go | 32 - vendor/github.com/mxk/go-flowrate/LICENSE | 29 - .../mxk/go-flowrate/flowrate/flowrate.go | 267 - .../github.com/mxk/go-flowrate/flowrate/io.go | 133 - .../mxk/go-flowrate/flowrate/util.go | 67 - .../onsi/gomega/gcustom/make_matcher.go | 270 - .../openshift/api/.ci-operator.yaml | 4 - .../github.com/openshift/api/.gitattributes | 7 - vendor/github.com/openshift/api/.gitignore | 21 - .../github.com/openshift/api/Dockerfile.rhel8 | 23 - vendor/github.com/openshift/api/Makefile | 181 - vendor/github.com/openshift/api/OWNERS | 19 - vendor/github.com/openshift/api/README.md | 90 - .../openshift/api/apiserver/.codegen.yaml | 2 - .../openshift/api/apiserver/install.go | 22 - .../openshift/api/apiserver/v1/Makefile | 3 - ...piserver.openshift.io_apirequestcount.yaml | 254 - .../openshift/api/apiserver/v1/doc.go | 8 - .../openshift/api/apiserver/v1/register.go | 38 - .../v1/stable.apirequestcount.testsuite.yaml | 15 - .../api/apiserver/v1/types_apirequestcount.go | 171 - .../api/apiserver/v1/zz_generated.deepcopy.go | 202 - .../v1/zz_generated.swagger_doc_generated.go | 97 - vendor/github.com/openshift/api/apps/OWNERS | 3 - .../github.com/openshift/api/apps/install.go | 26 - .../openshift/api/apps/v1/consts.go | 108 - .../api/apps/v1/deprecated_consts.go | 38 - .../github.com/openshift/api/apps/v1/doc.go | 9 - .../openshift/api/apps/v1/generated.pb.go | 7461 ------- .../openshift/api/apps/v1/generated.proto | 490 - .../openshift/api/apps/v1/legacy.go | 28 - .../openshift/api/apps/v1/register.go | 45 - .../github.com/openshift/api/apps/v1/types.go | 537 - .../api/apps/v1/zz_generated.deepcopy.go | 682 - .../v1/zz_generated.swagger_doc_generated.go | 284 - .../v1/zz_prerelease_lifecycle_generated.go | 114 - .../openshift/api/authorization/install.go | 26 - ...enshift_01_rolebindingrestriction.crd.yaml | 158 - .../openshift/api/authorization/v1/Makefile | 3 - .../openshift/api/authorization/v1/codec.go | 139 - .../openshift/api/authorization/v1/doc.go | 9 - .../api/authorization/v1/generated.pb.go | 8812 -------- .../api/authorization/v1/generated.proto | 557 - .../openshift/api/authorization/v1/legacy.go | 43 - .../api/authorization/v1/register.go | 60 - ...able.rolebindingrestriction.testsuite.yaml | 14 - .../openshift/api/authorization/v1/types.go | 632 - .../authorization/v1/zz_generated.deepcopy.go | 994 - .../v1/zz_generated.swagger_doc_generated.go | 364 - vendor/github.com/openshift/api/build/OWNERS | 4 - .../github.com/openshift/api/build/install.go | 26 - .../openshift/api/build/v1/consts.go | 202 - .../github.com/openshift/api/build/v1/doc.go | 8 - .../openshift/api/build/v1/generated.pb.go | 17545 ---------------- .../openshift/api/build/v1/generated.proto | 1239 -- .../openshift/api/build/v1/legacy.go | 28 - .../openshift/api/build/v1/register.go | 47 - .../openshift/api/build/v1/types.go | 1469 -- .../api/build/v1/zz_generated.deepcopy.go | 1610 -- .../v1/zz_generated.swagger_doc_generated.go | 692 - .../openshift/api/cloudnetwork/OWNERS | 6 - .../openshift/api/cloudnetwork/install.go | 26 - .../v1/001-cloudprivateipconfig.crd.yaml | 107 - .../001-cloudprivateipconfig.crd.yaml-patch | 10 - .../openshift/api/cloudnetwork/v1/Makefile | 3 - .../openshift/api/cloudnetwork/v1/doc.go | 5 - .../api/cloudnetwork/v1/generated.pb.go | 1045 - .../api/cloudnetwork/v1/generated.proto | 87 - .../openshift/api/cloudnetwork/v1/register.go | 37 - ...stable.cloudprivateipconfig.testsuite.yaml | 18 - .../openshift/api/cloudnetwork/v1/types.go | 91 - .../cloudnetwork/v1/zz_generated.deepcopy.go | 111 - .../v1/zz_generated.swagger_doc_generated.go | 54 - .../openshift/api/config/.codegen.yaml | 8 - .../openshift/api/config/install.go | 27 - ...or_01_backup-TechPreviewNoUpgrade.crd.yaml | 100 - ...ig-operator_01_insightsdatagather.crd.yaml | 62 - .../openshift/api/config/v1alpha1/Makefile | 3 - .../openshift/api/config/v1alpha1/doc.go | 8 - .../openshift/api/config/v1alpha1/register.go | 40 - .../techpreview.backup.testsuite.yaml | 202 - ...hpreview.insightsdatagather.testsuite.yaml | 14 - .../api/config/v1alpha1/types_backup.go | 168 - .../api/config/v1alpha1/types_insights.go | 82 - .../config/v1alpha1/zz_generated.deepcopy.go | 294 - .../zz_generated.swagger_doc_generated.go | 121 - .../openshift/api/console/.codegen.yaml | 2 - .../github.com/openshift/api/console/OWNERS | 3 - .../openshift/api/console/install.go | 27 - .../console/v1/00_consoleclidownload.crd.yaml | 77 - .../v1/00_consoleexternalloglink.crd.yaml | 68 - .../api/console/v1/00_consolelink.crd.yaml | 125 - .../v1/00_consolenotification.crd.yaml | 84 - .../console/v1/00_consolequickstart.crd.yaml | 165 - .../api/console/v1/00_consolesample.crd.yaml | 167 - .../console/v1/00_consoleyamlsample.crd.yaml | 74 - .../api/console/v1/90_consoleplugin.crd.yaml | 294 - .../openshift/api/console/v1/Makefile | 3 - .../openshift/api/console/v1/doc.go | 7 - .../openshift/api/console/v1/register.go | 53 - .../stable.consoleclidownload.testsuite.yaml | 20 - ...able.consoleexternalloglink.testsuite.yaml | 18 - .../v1/stable.consolelink.testsuite.yaml | 20 - .../stable.consolenotification.testsuite.yaml | 16 - .../v1/stable.consoleplugin.testsuite.yaml | 88 - .../stable.consolequickstart.testsuite.yaml | 28 - .../v1/stable.consolesample.testsuite.yaml | 183 - .../stable.consoleyamlsample.testsuite.yaml | 26 - .../openshift/api/console/v1/types.go | 10 - .../console/v1/types_console_cli_download.go | 54 - .../v1/types_console_external_log_links.go | 65 - .../api/console/v1/types_console_link.go | 94 - .../console/v1/types_console_notification.go | 68 - .../api/console/v1/types_console_plugin.go | 244 - .../console/v1/types_console_quick_start.go | 143 - .../api/console/v1/types_console_sample.go | 266 - .../console/v1/types_console_yaml_sample.go | 67 - .../api/console/v1/zz_generated.deepcopy.go | 1032 - .../v1/zz_generated.swagger_doc_generated.go | 460 - .../v1alpha1/90_consoleplugin.crd.yaml | 294 - .../openshift/api/console/v1alpha1/Makefile | 3 - .../openshift/api/console/v1alpha1/doc.go | 6 - .../api/console/v1alpha1/register.go | 39 - .../stable.consoleplugin.testsuite.yaml | 23 - .../openshift/api/console/v1alpha1/types.go | 1 - .../console/v1alpha1/types_console_plugin.go | 174 - .../console/v1alpha1/zz_generated.deepcopy.go | 141 - .../zz_generated.swagger_doc_generated.go | 79 - .../openshift/api/helm/.codegen.yaml | 2 - .../github.com/openshift/api/helm/install.go | 26 - .../v1beta1/00_helm-chart-repository.crd.yaml | 130 - .../00_project-helm-chart-repository.crd.yaml | 139 - .../openshift/api/helm/v1beta1/Makefile | 3 - .../openshift/api/helm/v1beta1/doc.go | 8 - .../openshift/api/helm/v1beta1/register.go | 40 - .../stable.helmchartrepository.testsuite.yaml | 14 - ....projecthelmchartrepository.testsuite.yaml | 14 - .../v1beta1/types_helm_chart_repository.go | 99 - .../types_project_helm_chart_repository.go | 99 - .../api/helm/v1beta1/zz_generated.deepcopy.go | 227 - .../zz_generated.swagger_doc_generated.go | 107 - .../openshift/api/image/.codegen.yaml | 2 - vendor/github.com/openshift/api/image/OWNERS | 5 - .../openshift/api/image/docker10/doc.go | 4 - .../openshift/api/image/docker10/register.go | 47 - .../api/image/docker10/types_docker.go | 60 - .../image/docker10/zz_generated.deepcopy.go | 114 - .../zz_generated.swagger_doc_generated.go | 30 - .../api/image/dockerpre012/deepcopy.go | 18 - .../openshift/api/image/dockerpre012/doc.go | 4 - .../api/image/dockerpre012/register.go | 46 - .../api/image/dockerpre012/types_docker.go | 140 - .../dockerpre012/zz_generated.deepcopy.go | 217 - .../zz_generated.swagger_doc_generated.go | 55 - .../github.com/openshift/api/image/install.go | 26 - .../openshift/api/image/v1/consts.go | 69 - .../github.com/openshift/api/image/v1/doc.go | 8 - .../openshift/api/image/v1/generated.pb.go | 11572 ---------- .../openshift/api/image/v1/generated.proto | 746 - .../openshift/api/image/v1/legacy.go | 33 - .../openshift/api/image/v1/register.go | 54 - .../openshift/api/image/v1/types.go | 766 - .../api/image/v1/zz_generated.deepcopy.go | 1045 - .../v1/zz_generated.swagger_doc_generated.go | 444 - .../openshift/api/imageregistry/.codegen.yaml | 2 - .../openshift/api/imageregistry/install.go | 26 - .../v1/00_imageregistry.crd.yaml | 1351 -- .../v1/00_imageregistry.crd.yaml-patch | 13 - .../imageregistry/v1/01_imagepruner.crd.yaml | 644 - .../openshift/api/imageregistry/v1/Makefile | 3 - .../openshift/api/imageregistry/v1/doc.go | 3 - .../api/imageregistry/v1/register.go | 48 - .../v1/stable.config.testsuite.yaml | 109 - .../v1/stable.imagepruner.testsuite.yaml | 15 - .../openshift/api/imageregistry/v1/types.go | 587 - .../api/imageregistry/v1/types_imagepruner.go | 112 - .../imageregistry/v1/zz_generated.deepcopy.go | 679 - .../v1/zz_generated.swagger_doc_generated.go | 333 - vendor/github.com/openshift/api/install.go | 167 - .../api/kubecontrolplane/.codegen.yaml | 2 - .../openshift/api/kubecontrolplane/install.go | 26 - .../openshift/api/kubecontrolplane/v1/doc.go | 7 - .../api/kubecontrolplane/v1/register.go | 38 - .../api/kubecontrolplane/v1/types.go | 217 - .../v1/zz_generated.deepcopy.go | 379 - .../v1/zz_generated.swagger_doc_generated.go | 161 - .../openshift/api/legacyconfig/v1/doc.go | 7 - .../openshift/api/legacyconfig/v1/register.go | 46 - .../api/legacyconfig/v1/serialization.go | 87 - .../api/legacyconfig/v1/stringsource.go | 31 - .../openshift/api/legacyconfig/v1/types.go | 1596 -- .../legacyconfig/v1/zz_generated.deepcopy.go | 2143 -- .../v1/zz_generated.swagger_doc_generated.go | 977 - .../openshift/api/machine/.codegen.yaml | 8 - .../github.com/openshift/api/machine/OWNERS | 4 - .../openshift/api/machine/install.go | 32 - ...olplanemachineset-CustomNoUpgrade.crd.yaml | 604 - ...10_controlplanemachineset-Default.crd.yaml | 590 - ...nemachineset-TechPreviewNoUpgrade.crd.yaml | 604 - .../openshift/api/machine/v1/Makefile | 3 - .../openshift/api/machine/v1/common.go | 13 - ...stom.controlplanemachineset.testsuite.yaml | 50 - .../openshift/api/machine/v1/doc.go | 7 - .../openshift/api/machine/v1/register.go | 40 - ....controlplanemachineset.aws.testsuite.yaml | 368 - ...ontrolplanemachineset.azure.testsuite.yaml | 74 - ....controlplanemachineset.gcp.testsuite.yaml | 74 - ...olplanemachineset.openstack.testsuite.yaml | 632 - ...able.controlplanemachineset.testsuite.yaml | 488 - ...view.controlplanemachineset.testsuite.yaml | 50 - .../api/machine/v1/types_alibabaprovider.go | 374 - .../openshift/api/machine/v1/types_aws.go | 49 - .../v1/types_controlplanemachineset.go | 461 - .../api/machine/v1/types_nutanixprovider.go | 177 - .../api/machine/v1/types_powervsprovider.go | 227 - .../api/machine/v1/zz_generated.deepcopy.go | 993 - .../v1/zz_generated.swagger_doc_generated.go | 433 - .../openshift/api/machine/v1alpha1/doc.go | 7 - .../api/machine/v1alpha1/register.go | 38 - .../api/machine/v1alpha1/types_openstack.go | 439 - .../machine/v1alpha1/zz_generated.deepcopy.go | 407 - .../zz_generated.swagger_doc_generated.go | 228 - .../machine/v1beta1/0000_10_machine.crd.yaml | 329 - .../v1beta1/0000_10_machinehealthcheck.yaml | 195 - .../v1beta1/0000_10_machineset.crd.yaml | 351 - .../openshift/api/machine/v1beta1/Makefile | 3 - .../openshift/api/machine/v1beta1/doc.go | 7 - .../openshift/api/machine/v1beta1/register.go | 44 - .../v1beta1/stable.machine.testsuite.yaml | 14 - .../stable.machinehealthcheck.testsuite.yaml | 16 - .../v1beta1/stable.machineset.testsuite.yaml | 15 - .../api/machine/v1beta1/types_awsprovider.go | 311 - .../machine/v1beta1/types_azureprovider.go | 568 - .../api/machine/v1beta1/types_gcpprovider.go | 284 - .../api/machine/v1beta1/types_machine.go | 388 - .../v1beta1/types_machinehealthcheck.go | 142 - .../api/machine/v1beta1/types_machineset.go | 145 - .../api/machine/v1beta1/types_provider.go | 229 - .../machine/v1beta1/types_vsphereprovider.go | 210 - .../machine/v1beta1/zz_generated.deepcopy.go | 1856 -- .../zz_generated.swagger_doc_generated.go | 808 - .../openshift/api/monitoring/.codegen.yaml | 8 - .../openshift/api/monitoring/install.go | 26 - ...00_50_monitoring_01_alertingrules.crd.yaml | 122 - ...monitoring_02_alertrelabelconfigs.crd.yaml | 140 - .../api/monitoring/v1alpha1/Makefile | 3 - .../openshift/api/monitoring/v1alpha1/doc.go | 6 - .../api/monitoring/v1alpha1/register.go | 41 - .../techpreview.alertingrule.testsuite.yaml | 24 - ...hpreview.alertrelabelconfig.testsuite.yaml | 20 - .../api/monitoring/v1alpha1/types.go | 349 - .../v1alpha1/zz_generated.deepcopy.go | 314 - .../zz_generated.swagger_doc_generated.go | 141 - .../openshift/api/network/.codegen.yaml | 6 - .../github.com/openshift/api/network/OWNERS | 4 - .../openshift/api/network/install.go | 27 - .../network/v1/001-clusternetwork-crd.yaml | 102 - .../api/network/v1/002-hostsubnet-crd.yaml | 88 - .../api/network/v1/003-netnamespace-crd.yaml | 66 - .../v1/004-egressnetworkpolicy-crd.yaml | 71 - .../openshift/api/network/v1/Makefile | 3 - .../openshift/api/network/v1/constants.go | 17 - .../openshift/api/network/v1/doc.go | 8 - .../openshift/api/network/v1/generated.pb.go | 3186 --- .../openshift/api/network/v1/generated.proto | 243 - .../openshift/api/network/v1/legacy.go | 27 - .../openshift/api/network/v1/register.go | 44 - .../v1/stable.clusternetwork.testsuite.yaml | 16 - .../stable.egressnetworkpolicy.testsuite.yaml | 16 - .../v1/stable.hostsubnet.testsuite.yaml | 18 - .../v1/stable.netnamespace.testsuite.yaml | 16 - .../openshift/api/network/v1/types.go | 300 - .../api/network/v1/zz_generated.deepcopy.go | 347 - .../v1/zz_generated.swagger_doc_generated.go | 145 - ...nsnameresolver_00-customnoupgrade.crd.yaml | 154 - ...resolver_00-customnoupgrade.crd.yaml-patch | 5 - ...70_dnsnameresolver_00-techpreview.crd.yaml | 154 - ...nameresolver_00-techpreview.crd.yaml-patch | 5 - .../openshift/api/network/v1alpha1/Makefile | 3 - .../custom.dnsnameresolver.testsuite.yaml | 402 - .../openshift/api/network/v1alpha1/doc.go | 6 - .../api/network/v1alpha1/register.go | 40 - ...techpreview.dnsnameresolver.testsuite.yaml | 402 - .../network/v1alpha1/types_dnsnameresolver.go | 139 - .../network/v1alpha1/zz_generated.deepcopy.go | 161 - .../zz_generated.swagger_doc_generated.go | 76 - .../api/networkoperator/.codegen.yaml | 2 - .../openshift/api/networkoperator/OWNERS | 5 - .../openshift/api/networkoperator/install.go | 26 - .../v1/001-egressrouter.crd.yaml | 208 - .../v1/001-egressrouter.crd.yaml-patch | 26 - .../openshift/api/networkoperator/v1/Makefile | 3 - .../openshift/api/networkoperator/v1/doc.go | 5 - .../api/networkoperator/v1/generated.pb.go | 2552 --- .../api/networkoperator/v1/generated.proto | 189 - .../api/networkoperator/v1/register.go | 25 - .../v1/stable.egressrouter.testsuite.yaml | 23 - .../networkoperator/v1/types_egressrouter.go | 265 - .../v1/zz_generated.deepcopy.go | 224 - .../v1/zz_generated.swagger_doc_generated.go | 119 - .../openshift/api/oauth/.codegen.yaml | 2 - .../github.com/openshift/api/oauth/install.go | 26 - .../github.com/openshift/api/oauth/v1/doc.go | 8 - .../openshift/api/oauth/v1/generated.pb.go | 4624 ---- .../openshift/api/oauth/v1/generated.proto | 321 - .../openshift/api/oauth/v1/legacy.go | 30 - .../openshift/api/oauth/v1/register.go | 47 - .../openshift/api/oauth/v1/types.go | 341 - .../api/oauth/v1/zz_generated.deepcopy.go | 447 - .../v1/zz_generated.swagger_doc_generated.go | 171 - .../api/openshiftcontrolplane/.codegen.yaml | 2 - .../api/openshiftcontrolplane/install.go | 26 - .../api/openshiftcontrolplane/v1/doc.go | 7 - .../api/openshiftcontrolplane/v1/register.go | 40 - .../api/openshiftcontrolplane/v1/types.go | 453 - .../v1/zz_generated.deepcopy.go | 679 - .../v1/zz_generated.swagger_doc_generated.go | 257 - .../openshift/api/operator/.codegen.yaml | 8 - .../openshift/api/operator/install.go | 27 - ...0000_10_config-operator_01_config.crd.yaml | 136 - ...or_01_config-TechPreviewNoUpgrade.crd.yaml | 207 - .../0000_12_etcd-operator_01_config.crd.yaml | 200 - ...kube-apiserver-operator_01_config.crd.yaml | 205 - ...piserver-operator_01_config.crd.yaml-patch | 3 - ...roller-manager-operator_01_config.crd.yaml | 198 - ...-manager-operator_01_config.crd.yaml-patch | 3 - ...kube-scheduler-operator_01_config.crd.yaml | 194 - ...cheduler-operator_01_config.crd.yaml-patch | 3 - ...hift-apiserver-operator_01_config.crd.yaml | 141 - ...oud-credential-operator_00_config.crd.yaml | 144 - ...rsion-migrator-operator_00_config.crd.yaml | 133 - ...authentication-operator_01_config.crd.yaml | 140 - ...roller-manager-operator_02_config.crd.yaml | 134 - ...00_50_cluster_storage_operator_01_crd.yaml | 144 - ...ess-operator_00-ingresscontroller.crd.yaml | 1137 - ...erator_00-ingresscontroller.crd.yaml-patch | 32 - ...ghts-operator_00-insightsoperator.crd.yaml | 260 - .../0000_50_service-ca-operator_02_crd.yaml | 135 - ...twork-operator_01-CustomNoUpgrade.crd.yaml | 564 - ...uster-network-operator_01-Default.crd.yaml | 561 - ...-operator_01-TechPreviewNoUpgrade.crd.yaml | 564 - .../v1/0000_70_dns-operator_00.crd.yaml | 305 - .../v1/0000_70_dns-operator_00.crd.yaml-patch | 21 - ...i_snapshot_controller_operator_01_crd.yaml | 134 - ...machine-config-operator_01_config.crd.yaml | 192 - ...0_90_cluster_csi_driver_01_config.crd.yaml | 271 - ...luster_csi_driver_01_config.crd.yaml-patch | 22 - .../operator/v1/00_console-operator.crd.yaml | 438 - .../openshift/api/operator/v1/Makefile | 3 - .../operator/v1/custom.network.testsuite.yaml | 100 - .../openshift/api/operator/v1/doc.go | 7 - .../openshift/api/operator/v1/register.go | 80 - .../v1/stable.authentication.testsuite.yaml | 16 - .../v1/stable.cloudcredential.testsuite.yaml | 16 - .../v1/stable.clustercsidriver.testsuite.yaml | 41 - .../operator/v1/stable.config.testsuite.yaml | 16 - .../operator/v1/stable.console.testsuite.yaml | 157 - ...table.csisnapshotcontroller.testsuite.yaml | 16 - .../api/operator/v1/stable.dns.testsuite.yaml | 21 - .../operator/v1/stable.etcd.testsuite.yaml | 16 - .../stable.ingresscontroller.testsuite.yaml | 478 - .../v1/stable.insightsoperator.testsuite.yaml | 16 - .../v1/stable.kubeapiserver.testsuite.yaml | 16 - ...table.kubecontrollermanager.testsuite.yaml | 17 - .../v1/stable.kubescheduler.testsuite.yaml | 16 - ....kubestorageversionmigrator.testsuite.yaml | 16 - ...stable.machineconfiguration.testsuite.yaml | 16 - .../operator/v1/stable.network.testsuite.yaml | 409 - .../stable.openshiftapiserver.testsuite.yaml | 16 - ....openshiftcontrollermanager.testsuite.yaml | 16 - .../v1/stable.serviceca.testsuite.yaml | 16 - .../operator/v1/stable.storage.testsuite.yaml | 113 - .../v1/techpreview.etcd.testsuite.yaml | 62 - .../v1/techpreview.network.testsuite.yaml | 100 - .../openshift/api/operator/v1/types.go | 237 - .../api/operator/v1/types_authentication.go | 63 - .../api/operator/v1/types_cloudcredential.go | 87 - .../openshift/api/operator/v1/types_config.go | 56 - .../api/operator/v1/types_console.go | 385 - .../operator/v1/types_csi_cluster_driver.go | 300 - .../api/operator/v1/types_csi_snapshot.go | 57 - .../openshift/api/operator/v1/types_dns.go | 529 - .../openshift/api/operator/v1/types_etcd.go | 81 - .../api/operator/v1/types_ingress.go | 1881 -- .../api/operator/v1/types_insights.go | 151 - .../api/operator/v1/types_kubeapiserver.go | 78 - .../v1/types_kubecontrollermanager.go | 63 - .../v1/types_kubestorageversionmigrator.go | 52 - .../operator/v1/types_machineconfiguration.go | 58 - .../api/operator/v1/types_network.go | 720 - .../operator/v1/types_openshiftapiserver.go | 62 - .../v1/types_openshiftcontrollermanager.go | 52 - .../api/operator/v1/types_scheduler.go | 55 - .../api/operator/v1/types_serviceca.go | 54 - .../v1/types_servicecatalogapiserver.go | 54 - .../types_servicecatalogcontrollermanager.go | 54 - .../api/operator/v1/types_storage.go | 76 - .../api/operator/v1/zz_generated.deepcopy.go | 4592 ---- .../v1/zz_generated.swagger_doc_generated.go | 1768 -- ...1_etcdbackup-TechPreviewNoUpgrade.crd.yaml | 114 - ...rator_01_imagecontentsourcepolicy.crd.yaml | 59 - ...g-operator_01_olm-CustomNoUpgrade.crd.yaml | 140 - ...rator_01_olm-TechPreviewNoUpgrade.crd.yaml | 140 - .../openshift/api/operator/v1alpha1/Makefile | 3 - .../v1alpha1/custom.olm.testsuite.yaml | 28 - .../openshift/api/operator/v1alpha1/doc.go | 6 - .../api/operator/v1alpha1/register.go | 45 - ...le.imagecontentsourcepolicy.testsuite.yaml | 14 - .../techpreview.etcdbackup.testsuite.yaml | 38 - .../v1alpha1/techpreview.olm.testsuite.yaml | 28 - .../openshift/api/operator/v1alpha1/types.go | 204 - .../api/operator/v1alpha1/types_etcdbackup.go | 101 - .../types_image_content_source_policy.go | 79 - .../api/operator/v1alpha1/types_olm.go | 56 - .../v1alpha1/zz_generated.deepcopy.go | 562 - .../zz_generated.swagger_doc_generated.go | 242 - .../api/operatorcontrolplane/.codegen.yaml | 2 - .../api/operatorcontrolplane/install.go | 26 - ...10-pod-network-connectivity-check.crd.yaml | 227 - .../operatorcontrolplane/v1alpha1/Makefile | 3 - .../api/operatorcontrolplane/v1alpha1/doc.go | 8 - .../operatorcontrolplane/v1alpha1/register.go | 39 - ...podnetworkconnectivitycheck.testsuite.yaml | 18 - .../v1alpha1/types_conditioncheck.go | 193 - .../v1alpha1/zz_generated.deepcopy.go | 199 - .../zz_generated.swagger_doc_generated.go | 95 - .../github.com/openshift/api/osin/install.go | 26 - .../github.com/openshift/api/osin/v1/doc.go | 7 - .../openshift/api/osin/v1/register.go | 50 - .../github.com/openshift/api/osin/v1/types.go | 488 - .../api/osin/v1/zz_generated.deepcopy.go | 645 - .../v1/zz_generated.swagger_doc_generated.go | 280 - .../api/pkg/serialization/serialization.go | 45 - .../github.com/openshift/api/project/OWNERS | 2 - .../openshift/api/project/install.go | 26 - .../openshift/api/project/v1/doc.go | 8 - .../openshift/api/project/v1/generated.pb.go | 1305 -- .../openshift/api/project/v1/generated.proto | 90 - .../openshift/api/project/v1/legacy.go | 23 - .../openshift/api/project/v1/register.go | 40 - .../openshift/api/project/v1/types.go | 111 - .../api/project/v1/zz_generated.deepcopy.go | 142 - .../v1/zz_generated.swagger_doc_generated.go | 65 - vendor/github.com/openshift/api/quota/OWNERS | 3 - .../github.com/openshift/api/quota/install.go | 26 - ...openshift_01_clusterresourcequota.crd.yaml | 197 - .../openshift/api/quota/v1/Makefile | 3 - .../github.com/openshift/api/quota/v1/doc.go | 8 - .../openshift/api/quota/v1/generated.pb.go | 2152 -- .../openshift/api/quota/v1/generated.proto | 124 - .../openshift/api/quota/v1/legacy.go | 24 - .../openshift/api/quota/v1/register.go | 41 - ...stable.clusterresourcequota.testsuite.yaml | 18 - .../openshift/api/quota/v1/types.go | 139 - .../api/quota/v1/zz_generated.deepcopy.go | 242 - .../v1/zz_generated.swagger_doc_generated.go | 96 - .../openshift/api/route/.codegen.yaml | 8 - vendor/github.com/openshift/api/route/OWNERS | 5 - .../github.com/openshift/api/route/install.go | 26 - .../openshift/api/samples/.codegen.yaml | 2 - .../openshift/api/samples/install.go | 26 - .../api/samples/v1/00_samplesconfig.crd.yaml | 127 - .../openshift/api/samples/v1/Makefile | 3 - .../openshift/api/samples/v1/doc.go | 7 - .../openshift/api/samples/v1/generated.pb.go | 1847 -- .../openshift/api/samples/v1/generated.proto | 156 - .../openshift/api/samples/v1/register.go | 51 - .../samples/v1/stable.config.testsuite.yaml | 14 - .../openshift/api/samples/v1/types_config.go | 240 - .../api/samples/v1/zz_generated.deepcopy.go | 158 - .../v1/zz_generated.swagger_doc_generated.go | 74 - .../openshift/api/security/install.go | 26 - ...0000_03_security-openshift_01_scc.crd.yaml | 279 - .../openshift/api/security/v1/Makefile | 3 - .../openshift/api/security/v1/consts.go | 13 - .../openshift/api/security/v1/doc.go | 8 - .../openshift/api/security/v1/generated.pb.go | 5283 ----- .../openshift/api/security/v1/generated.proto | 380 - .../openshift/api/security/v1/legacy.go | 25 - .../openshift/api/security/v1/register.go | 44 - ....securitycontextconstraints.testsuite.yaml | 36 - .../openshift/api/security/v1/types.go | 468 - .../api/security/v1/zz_generated.deepcopy.go | 533 - .../v1/zz_generated.swagger_doc_generated.go | 228 - .../api/servicecertsigner/.codegen.yaml | 2 - .../api/servicecertsigner/install.go | 26 - .../api/servicecertsigner/v1alpha1/doc.go | 6 - .../servicecertsigner/v1alpha1/register.go | 40 - .../api/servicecertsigner/v1alpha1/types.go | 53 - .../v1alpha1/zz_generated.deepcopy.go | 105 - .../zz_generated.swagger_doc_generated.go | 33 - .../api/sharedresource/.codegen.yaml | 2 - .../openshift/api/sharedresource/OWNERS | 5 - .../openshift/api/sharedresource/install.go | 26 - .../v1alpha1/0000_10_sharedconfigmap.crd.yaml | 105 - .../v1alpha1/0000_10_sharedsecret.crd.yaml | 105 - .../api/sharedresource/v1alpha1/Makefile | 3 - .../api/sharedresource/v1alpha1/doc.go | 7 - .../api/sharedresource/v1alpha1/register.go | 53 - .../stable.sharedconfigmap.testsuite.yaml | 20 - .../stable.sharedsecret.testsuite.yaml | 20 - .../v1alpha1/types_shared_configmap.go | 93 - .../v1alpha1/types_shared_secret.go | 93 - .../v1alpha1/zz_generated.deepcopy.go | 245 - .../zz_generated.swagger_doc_generated.go | 112 - .../github.com/openshift/api/template/OWNERS | 4 - .../openshift/api/template/install.go | 26 - .../openshift/api/template/v1/codec.go | 33 - .../openshift/api/template/v1/consts.go | 16 - .../openshift/api/template/v1/doc.go | 8 - .../openshift/api/template/v1/generated.pb.go | 4115 ---- .../openshift/api/template/v1/generated.proto | 262 - .../openshift/api/template/v1/legacy.go | 24 - .../openshift/api/template/v1/register.go | 43 - .../openshift/api/template/v1/types.go | 294 - .../api/template/v1/zz_generated.deepcopy.go | 394 - .../v1/zz_generated.swagger_doc_generated.go | 159 - .../github.com/openshift/api/user/install.go | 26 - .../github.com/openshift/api/user/v1/doc.go | 8 - .../openshift/api/user/v1/generated.pb.go | 2274 -- .../openshift/api/user/v1/generated.proto | 144 - .../openshift/api/user/v1/legacy.go | 27 - .../openshift/api/user/v1/register.go | 44 - .../github.com/openshift/api/user/v1/types.go | 174 - .../api/user/v1/zz_generated.deepcopy.go | 258 - .../v1/zz_generated.swagger_doc_generated.go | 90 - .../openshift/osde2e-common/LICENSE | 201 - .../pkg/clients/openshift/client.go | 152 - .../pkg/clients/openshift/errors.go | 46 - .../pkg/clients/openshift/healthcheck.go | 94 - .../pkg/clients/openshift/upgrade_operator.go | 262 - .../gomega/assertions/eventually_configmap.go | 21 - .../assertions/eventually_deployment.go | 21 - .../matchers/contain_item_with_prefix.go | 66 - .../gomega/matchers/deployment_available.go | 22 - .../golang.org/x/net/internal/socks/client.go | 168 - .../golang.org/x/net/internal/socks/socks.go | 317 - vendor/golang.org/x/net/proxy/dial.go | 54 - vendor/golang.org/x/net/proxy/direct.go | 31 - vendor/golang.org/x/net/proxy/per_host.go | 153 - vendor/golang.org/x/net/proxy/proxy.go | 149 - vendor/golang.org/x/net/proxy/socks5.go | 42 - vendor/golang.org/x/net/websocket/client.go | 139 - vendor/golang.org/x/net/websocket/dial.go | 29 - vendor/golang.org/x/net/websocket/hybi.go | 582 - vendor/golang.org/x/net/websocket/server.go | 113 - .../golang.org/x/net/websocket/websocket.go | 449 - vendor/k8s.io/api/imagepolicy/v1alpha1/doc.go | 23 - .../api/imagepolicy/v1alpha1/generated.pb.go | 1374 -- .../api/imagepolicy/v1alpha1/generated.proto | 89 - .../api/imagepolicy/v1alpha1/register.go | 51 - .../k8s.io/api/imagepolicy/v1alpha1/types.go | 83 - .../v1alpha1/types_swagger_doc_generated.go | 72 - .../v1alpha1/zz_generated.deepcopy.go | 121 - .../apimachinery/pkg/util/httpstream/doc.go | 19 - .../pkg/util/httpstream/httpstream.go | 189 - .../pkg/util/httpstream/spdy/connection.go | 204 - .../pkg/util/httpstream/spdy/roundtripper.go | 399 - .../pkg/util/httpstream/spdy/upgrade.go | 120 - .../pkg/util/httpstream/wsstream/conn.go | 452 - .../pkg/util/httpstream/wsstream/doc.go | 69 - .../pkg/util/httpstream/wsstream/stream.go | 177 - .../pkg/util/portforward/constants.go | 24 - .../apimachinery/pkg/util/proxy/dial.go | 122 - .../k8s.io/apimachinery/pkg/util/proxy/doc.go | 18 - .../apimachinery/pkg/util/proxy/transport.go | 272 - .../pkg/util/proxy/upgradeaware.go | 558 - .../pkg/util/remotecommand/constants.go | 67 - .../third_party/forked/golang/netutil/addr.go | 28 - .../client-go/tools/remotecommand/OWNERS | 10 - .../client-go/tools/remotecommand/doc.go | 20 - .../tools/remotecommand/errorstream.go | 54 - .../client-go/tools/remotecommand/fallback.go | 60 - .../client-go/tools/remotecommand/reader.go | 41 - .../tools/remotecommand/remotecommand.go | 58 - .../client-go/tools/remotecommand/resize.go | 33 - .../client-go/tools/remotecommand/spdy.go | 171 - .../client-go/tools/remotecommand/v1.go | 159 - .../client-go/tools/remotecommand/v2.go | 199 - .../client-go/tools/remotecommand/v3.go | 111 - .../client-go/tools/remotecommand/v4.go | 119 - .../client-go/tools/remotecommand/v5.go | 35 - .../tools/remotecommand/websocket.go | 516 - .../k8s.io/client-go/transport/spdy/spdy.go | 107 - .../transport/websocket/roundtripper.go | 224 - vendor/k8s.io/client-go/util/exec/exec.go | 52 - vendor/modules.txt | 106 - vendor/sigs.k8s.io/e2e-framework/LICENSE | 201 - .../e2e-framework/klient/conf/config.go | 152 - .../e2e-framework/klient/k8s/object.go | 46 - .../klient/k8s/resources/resources.go | 334 - .../e2e-framework/klient/k8s/watcher/watch.go | 132 - .../klient/wait/conditions/conditions.go | 305 - .../e2e-framework/klient/wait/wait.go | 107 - 625 files changed, 178339 deletions(-) delete mode 100644 vendor/github.com/gorilla/websocket/.gitignore delete mode 100644 vendor/github.com/gorilla/websocket/AUTHORS delete mode 100644 vendor/github.com/gorilla/websocket/LICENSE delete mode 100644 vendor/github.com/gorilla/websocket/README.md delete mode 100644 vendor/github.com/gorilla/websocket/client.go delete mode 100644 vendor/github.com/gorilla/websocket/compression.go delete mode 100644 vendor/github.com/gorilla/websocket/conn.go delete mode 100644 vendor/github.com/gorilla/websocket/doc.go delete mode 100644 vendor/github.com/gorilla/websocket/join.go delete mode 100644 vendor/github.com/gorilla/websocket/json.go delete mode 100644 vendor/github.com/gorilla/websocket/mask.go delete mode 100644 vendor/github.com/gorilla/websocket/mask_safe.go delete mode 100644 vendor/github.com/gorilla/websocket/prepared.go delete mode 100644 vendor/github.com/gorilla/websocket/proxy.go delete mode 100644 vendor/github.com/gorilla/websocket/server.go delete mode 100644 vendor/github.com/gorilla/websocket/util.go delete mode 100644 vendor/github.com/moby/spdystream/CONTRIBUTING.md delete mode 100644 vendor/github.com/moby/spdystream/LICENSE delete mode 100644 vendor/github.com/moby/spdystream/MAINTAINERS delete mode 100644 vendor/github.com/moby/spdystream/NOTICE delete mode 100644 vendor/github.com/moby/spdystream/README.md delete mode 100644 vendor/github.com/moby/spdystream/connection.go delete mode 100644 vendor/github.com/moby/spdystream/handlers.go delete mode 100644 vendor/github.com/moby/spdystream/priority.go delete mode 100644 vendor/github.com/moby/spdystream/spdy/dictionary.go delete mode 100644 vendor/github.com/moby/spdystream/spdy/read.go delete mode 100644 vendor/github.com/moby/spdystream/spdy/types.go delete mode 100644 vendor/github.com/moby/spdystream/spdy/write.go delete mode 100644 vendor/github.com/moby/spdystream/stream.go delete mode 100644 vendor/github.com/moby/spdystream/utils.go delete mode 100644 vendor/github.com/mxk/go-flowrate/LICENSE delete mode 100644 vendor/github.com/mxk/go-flowrate/flowrate/flowrate.go delete mode 100644 vendor/github.com/mxk/go-flowrate/flowrate/io.go delete mode 100644 vendor/github.com/mxk/go-flowrate/flowrate/util.go delete mode 100644 vendor/github.com/onsi/gomega/gcustom/make_matcher.go delete mode 100644 vendor/github.com/openshift/api/.ci-operator.yaml delete mode 100644 vendor/github.com/openshift/api/.gitattributes delete mode 100644 vendor/github.com/openshift/api/.gitignore delete mode 100644 vendor/github.com/openshift/api/Dockerfile.rhel8 delete mode 100644 vendor/github.com/openshift/api/Makefile delete mode 100644 vendor/github.com/openshift/api/OWNERS delete mode 100644 vendor/github.com/openshift/api/README.md delete mode 100644 vendor/github.com/openshift/api/apiserver/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/apiserver/install.go delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/apiserver.openshift.io_apirequestcount.yaml delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/register.go delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/stable.apirequestcount.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/types_apirequestcount.go delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/apiserver/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/apps/OWNERS delete mode 100644 vendor/github.com/openshift/api/apps/install.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/consts.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/deprecated_consts.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/apps/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/register.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/types.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/apps/v1/zz_prerelease_lifecycle_generated.go delete mode 100644 vendor/github.com/openshift/api/authorization/install.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/0000_03_authorization-openshift_01_rolebindingrestriction.crd.yaml delete mode 100644 vendor/github.com/openshift/api/authorization/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/authorization/v1/codec.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/authorization/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/register.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/stable.rolebindingrestriction.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/authorization/v1/types.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/authorization/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/build/OWNERS delete mode 100644 vendor/github.com/openshift/api/build/install.go delete mode 100644 vendor/github.com/openshift/api/build/v1/consts.go delete mode 100644 vendor/github.com/openshift/api/build/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/build/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/build/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/build/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/build/v1/register.go delete mode 100644 vendor/github.com/openshift/api/build/v1/types.go delete mode 100644 vendor/github.com/openshift/api/build/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/build/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/OWNERS delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/install.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/001-cloudprivateipconfig.crd.yaml delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/001-cloudprivateipconfig.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/register.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/stable.cloudprivateipconfig.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/types.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/cloudnetwork/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/config/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/config/install.go delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/0000_10_config-operator_01_backup-TechPreviewNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/0000_10_config-operator_01_insightsdatagather.crd.yaml delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/techpreview.backup.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/techpreview.insightsdatagather.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/types_backup.go delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/types_insights.go delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/config/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/console/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/console/OWNERS delete mode 100644 vendor/github.com/openshift/api/console/install.go delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consoleclidownload.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consoleexternalloglink.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consolelink.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consolenotification.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consolequickstart.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consolesample.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/00_consoleyamlsample.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/90_consoleplugin.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/console/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/console/v1/register.go delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consoleclidownload.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consoleexternalloglink.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consolelink.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consolenotification.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consoleplugin.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consolequickstart.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consolesample.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/stable.consoleyamlsample.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1/types.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_cli_download.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_external_log_links.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_link.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_notification.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_plugin.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_quick_start.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_sample.go delete mode 100644 vendor/github.com/openshift/api/console/v1/types_console_yaml_sample.go delete mode 100644 vendor/github.com/openshift/api/console/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/console/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/90_consoleplugin.crd.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/stable.consoleplugin.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/types.go delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/types_console_plugin.go delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/console/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/helm/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/helm/install.go delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/00_helm-chart-repository.crd.yaml delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/00_project-helm-chart-repository.crd.yaml delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/Makefile delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/doc.go delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/register.go delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/stable.helmchartrepository.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/stable.projecthelmchartrepository.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/types_helm_chart_repository.go delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/types_project_helm_chart_repository.go delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/helm/v1beta1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/image/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/image/OWNERS delete mode 100644 vendor/github.com/openshift/api/image/docker10/doc.go delete mode 100644 vendor/github.com/openshift/api/image/docker10/register.go delete mode 100644 vendor/github.com/openshift/api/image/docker10/types_docker.go delete mode 100644 vendor/github.com/openshift/api/image/docker10/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/image/docker10/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/image/dockerpre012/deepcopy.go delete mode 100644 vendor/github.com/openshift/api/image/dockerpre012/doc.go delete mode 100644 vendor/github.com/openshift/api/image/dockerpre012/register.go delete mode 100644 vendor/github.com/openshift/api/image/dockerpre012/types_docker.go delete mode 100644 vendor/github.com/openshift/api/image/dockerpre012/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/image/dockerpre012/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/image/install.go delete mode 100644 vendor/github.com/openshift/api/image/v1/consts.go delete mode 100644 vendor/github.com/openshift/api/image/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/image/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/image/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/image/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/image/v1/register.go delete mode 100644 vendor/github.com/openshift/api/image/v1/types.go delete mode 100644 vendor/github.com/openshift/api/image/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/image/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/imageregistry/install.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/00_imageregistry.crd.yaml delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/00_imageregistry.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/01_imagepruner.crd.yaml delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/register.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/stable.config.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/stable.imagepruner.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/types.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/types_imagepruner.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/imageregistry/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/install.go delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/install.go delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/v1/register.go delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/v1/types.go delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/kubecontrolplane/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/register.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/serialization.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/stringsource.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/types.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/legacyconfig/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/machine/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/machine/OWNERS delete mode 100644 vendor/github.com/openshift/api/machine/install.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/0000_10_controlplanemachineset-CustomNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/0000_10_controlplanemachineset-Default.crd.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/0000_10_controlplanemachineset-TechPreviewNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/machine/v1/common.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/custom.controlplanemachineset.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/register.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/stable.controlplanemachineset.aws.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/stable.controlplanemachineset.azure.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/stable.controlplanemachineset.gcp.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/stable.controlplanemachineset.openstack.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/stable.controlplanemachineset.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/techpreview.controlplanemachineset.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1/types_alibabaprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/types_aws.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/types_nutanixprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/types_powervsprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/machine/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/machine/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/machine/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/machine/v1alpha1/types_openstack.go delete mode 100644 vendor/github.com/openshift/api/machine/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/machine/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/0000_10_machine.crd.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/0000_10_machinehealthcheck.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/0000_10_machineset.crd.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/Makefile delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/doc.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/register.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/stable.machine.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/stable.machinehealthcheck.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/stable.machineset.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_azureprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_gcpprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_machine.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_machinehealthcheck.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_machineset.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_provider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/types_vsphereprovider.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/monitoring/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/monitoring/install.go delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/0000_50_monitoring_01_alertingrules.crd.yaml delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/0000_50_monitoring_02_alertrelabelconfigs.crd.yaml delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/techpreview.alertingrule.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/techpreview.alertrelabelconfig.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/types.go delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/monitoring/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/network/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/network/OWNERS delete mode 100644 vendor/github.com/openshift/api/network/install.go delete mode 100644 vendor/github.com/openshift/api/network/v1/001-clusternetwork-crd.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/002-hostsubnet-crd.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/003-netnamespace-crd.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/004-egressnetworkpolicy-crd.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/network/v1/constants.go delete mode 100644 vendor/github.com/openshift/api/network/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/network/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/network/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/network/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/network/v1/register.go delete mode 100644 vendor/github.com/openshift/api/network/v1/stable.clusternetwork.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/stable.egressnetworkpolicy.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/stable.hostsubnet.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/stable.netnamespace.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1/types.go delete mode 100644 vendor/github.com/openshift/api/network/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/network/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/0000_70_dnsnameresolver_00-customnoupgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/0000_70_dnsnameresolver_00-customnoupgrade.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/0000_70_dnsnameresolver_00-techpreview.crd.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/0000_70_dnsnameresolver_00-techpreview.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/custom.dnsnameresolver.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/techpreview.dnsnameresolver.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/types_dnsnameresolver.go delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/network/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/networkoperator/OWNERS delete mode 100644 vendor/github.com/openshift/api/networkoperator/install.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/001-egressrouter.crd.yaml delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/001-egressrouter.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/register.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/stable.egressrouter.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/types_egressrouter.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/networkoperator/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/oauth/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/oauth/install.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/oauth/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/register.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/types.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/oauth/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/install.go delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/v1/register.go delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/v1/types.go delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/openshiftcontrolplane/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/operator/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/operator/install.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_10_config-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_12_etcd-operator_01_config-TechPreviewNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_12_etcd-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_20_kube-apiserver-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_20_kube-apiserver-operator_01_config.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_25_kube-controller-manager-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_25_kube-controller-manager-operator_01_config.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_25_kube-scheduler-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_25_kube-scheduler-operator_01_config.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_30_openshift-apiserver-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_40_cloud-credential-operator_00_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_40_kube-storage-version-migrator-operator_00_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_cluster-authentication-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_cluster-openshift-controller-manager-operator_02_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_cluster_storage_operator_01_crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_ingress-operator_00-ingresscontroller.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_ingress-operator_00-ingresscontroller.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_insights-operator_00-insightsoperator.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_50_service-ca-operator_02_crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_70_cluster-network-operator_01-CustomNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_70_cluster-network-operator_01-Default.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_70_cluster-network-operator_01-TechPreviewNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_70_dns-operator_00.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_70_dns-operator_00.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_80_csi_snapshot_controller_operator_01_crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_80_machine-config-operator_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_90_cluster_csi_driver_01_config.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/0000_90_cluster_csi_driver_01_config.crd.yaml-patch delete mode 100644 vendor/github.com/openshift/api/operator/v1/00_console-operator.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/operator/v1/custom.network.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/register.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.authentication.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.cloudcredential.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.clustercsidriver.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.config.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.console.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.csisnapshotcontroller.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.dns.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.etcd.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.ingresscontroller.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.insightsoperator.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.kubeapiserver.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.kubecontrollermanager.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.kubescheduler.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.kubestorageversionmigrator.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.machineconfiguration.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.network.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.openshiftapiserver.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.openshiftcontrollermanager.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.serviceca.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/stable.storage.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/techpreview.etcd.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/techpreview.network.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1/types.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_authentication.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_cloudcredential.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_config.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_console.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_csi_cluster_driver.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_csi_snapshot.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_dns.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_etcd.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_ingress.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_insights.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_kubeapiserver.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_kubecontrollermanager.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_kubestorageversionmigrator.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_machineconfiguration.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_network.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_openshiftapiserver.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_openshiftcontrollermanager.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_scheduler.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_serviceca.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_servicecatalogapiserver.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_servicecatalogcontrollermanager.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/types_storage.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/0000_10_01_etcdbackup-TechPreviewNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/0000_10_config-operator_01_imagecontentsourcepolicy.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/0000_10_config-operator_01_olm-CustomNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/0000_10_config-operator_01_olm-TechPreviewNoUpgrade.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/custom.olm.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/stable.imagecontentsourcepolicy.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/techpreview.etcdbackup.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/techpreview.olm.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/types.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/types_etcdbackup.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/types_image_content_source_policy.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/types_olm.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/install.go delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/0000_10-pod-network-connectivity-check.crd.yaml delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/stable.podnetworkconnectivitycheck.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/types_conditioncheck.go delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/osin/install.go delete mode 100644 vendor/github.com/openshift/api/osin/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/osin/v1/register.go delete mode 100644 vendor/github.com/openshift/api/osin/v1/types.go delete mode 100644 vendor/github.com/openshift/api/osin/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/osin/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/pkg/serialization/serialization.go delete mode 100644 vendor/github.com/openshift/api/project/OWNERS delete mode 100644 vendor/github.com/openshift/api/project/install.go delete mode 100644 vendor/github.com/openshift/api/project/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/project/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/project/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/project/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/project/v1/register.go delete mode 100644 vendor/github.com/openshift/api/project/v1/types.go delete mode 100644 vendor/github.com/openshift/api/project/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/project/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/quota/OWNERS delete mode 100644 vendor/github.com/openshift/api/quota/install.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/0000_03_quota-openshift_01_clusterresourcequota.crd.yaml delete mode 100644 vendor/github.com/openshift/api/quota/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/quota/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/quota/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/register.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/stable.clusterresourcequota.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/quota/v1/types.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/quota/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/route/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/route/OWNERS delete mode 100644 vendor/github.com/openshift/api/route/install.go delete mode 100644 vendor/github.com/openshift/api/samples/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/samples/install.go delete mode 100644 vendor/github.com/openshift/api/samples/v1/00_samplesconfig.crd.yaml delete mode 100644 vendor/github.com/openshift/api/samples/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/samples/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/samples/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/samples/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/samples/v1/register.go delete mode 100644 vendor/github.com/openshift/api/samples/v1/stable.config.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/samples/v1/types_config.go delete mode 100644 vendor/github.com/openshift/api/samples/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/samples/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/security/install.go delete mode 100644 vendor/github.com/openshift/api/security/v1/0000_03_security-openshift_01_scc.crd.yaml delete mode 100644 vendor/github.com/openshift/api/security/v1/Makefile delete mode 100644 vendor/github.com/openshift/api/security/v1/consts.go delete mode 100644 vendor/github.com/openshift/api/security/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/security/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/security/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/security/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/security/v1/register.go delete mode 100644 vendor/github.com/openshift/api/security/v1/stable.securitycontextconstraints.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/security/v1/types.go delete mode 100644 vendor/github.com/openshift/api/security/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/security/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/install.go delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/v1alpha1/types.go delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/servicecertsigner/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/.codegen.yaml delete mode 100644 vendor/github.com/openshift/api/sharedresource/OWNERS delete mode 100644 vendor/github.com/openshift/api/sharedresource/install.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/0000_10_sharedconfigmap.crd.yaml delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/0000_10_sharedsecret.crd.yaml delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/Makefile delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/doc.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/register.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/stable.sharedconfigmap.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/stable.sharedsecret.testsuite.yaml delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_configmap.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_secret.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/sharedresource/v1alpha1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/template/OWNERS delete mode 100644 vendor/github.com/openshift/api/template/install.go delete mode 100644 vendor/github.com/openshift/api/template/v1/codec.go delete mode 100644 vendor/github.com/openshift/api/template/v1/consts.go delete mode 100644 vendor/github.com/openshift/api/template/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/template/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/template/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/template/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/template/v1/register.go delete mode 100644 vendor/github.com/openshift/api/template/v1/types.go delete mode 100644 vendor/github.com/openshift/api/template/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/template/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/api/user/install.go delete mode 100644 vendor/github.com/openshift/api/user/v1/doc.go delete mode 100644 vendor/github.com/openshift/api/user/v1/generated.pb.go delete mode 100644 vendor/github.com/openshift/api/user/v1/generated.proto delete mode 100644 vendor/github.com/openshift/api/user/v1/legacy.go delete mode 100644 vendor/github.com/openshift/api/user/v1/register.go delete mode 100644 vendor/github.com/openshift/api/user/v1/types.go delete mode 100644 vendor/github.com/openshift/api/user/v1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/openshift/api/user/v1/zz_generated.swagger_doc_generated.go delete mode 100644 vendor/github.com/openshift/osde2e-common/LICENSE delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/clients/openshift/client.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/clients/openshift/errors.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/clients/openshift/healthcheck.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/clients/openshift/upgrade_operator.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/gomega/assertions/eventually_configmap.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/gomega/assertions/eventually_deployment.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/gomega/matchers/contain_item_with_prefix.go delete mode 100644 vendor/github.com/openshift/osde2e-common/pkg/gomega/matchers/deployment_available.go delete mode 100644 vendor/golang.org/x/net/internal/socks/client.go delete mode 100644 vendor/golang.org/x/net/internal/socks/socks.go delete mode 100644 vendor/golang.org/x/net/proxy/dial.go delete mode 100644 vendor/golang.org/x/net/proxy/direct.go delete mode 100644 vendor/golang.org/x/net/proxy/per_host.go delete mode 100644 vendor/golang.org/x/net/proxy/proxy.go delete mode 100644 vendor/golang.org/x/net/proxy/socks5.go delete mode 100644 vendor/golang.org/x/net/websocket/client.go delete mode 100644 vendor/golang.org/x/net/websocket/dial.go delete mode 100644 vendor/golang.org/x/net/websocket/hybi.go delete mode 100644 vendor/golang.org/x/net/websocket/server.go delete mode 100644 vendor/golang.org/x/net/websocket/websocket.go delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/doc.go delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/generated.pb.go delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/generated.proto delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/register.go delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/types.go delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/types_swagger_doc_generated.go delete mode 100644 vendor/k8s.io/api/imagepolicy/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/wsstream/conn.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/wsstream/doc.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/wsstream/stream.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/portforward/constants.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/proxy/dial.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/proxy/doc.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/proxy/transport.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go delete mode 100644 vendor/k8s.io/apimachinery/pkg/util/remotecommand/constants.go delete mode 100644 vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/OWNERS delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/doc.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/errorstream.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/fallback.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/reader.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/remotecommand.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/resize.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/spdy.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v1.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v2.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v3.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v4.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v5.go delete mode 100644 vendor/k8s.io/client-go/tools/remotecommand/websocket.go delete mode 100644 vendor/k8s.io/client-go/transport/spdy/spdy.go delete mode 100644 vendor/k8s.io/client-go/transport/websocket/roundtripper.go delete mode 100644 vendor/k8s.io/client-go/util/exec/exec.go delete mode 100644 vendor/sigs.k8s.io/e2e-framework/LICENSE delete mode 100644 vendor/sigs.k8s.io/e2e-framework/klient/conf/config.go delete mode 100644 vendor/sigs.k8s.io/e2e-framework/klient/k8s/object.go delete mode 100644 vendor/sigs.k8s.io/e2e-framework/klient/k8s/resources/resources.go delete mode 100644 vendor/sigs.k8s.io/e2e-framework/klient/k8s/watcher/watch.go delete mode 100644 vendor/sigs.k8s.io/e2e-framework/klient/wait/conditions/conditions.go delete mode 100644 vendor/sigs.k8s.io/e2e-framework/klient/wait/wait.go diff --git a/go.mod b/go.mod index e9023f774..b6bfe13fa 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/onsi/gomega v1.36.1 github.com/openshift/api v0.0.0-20240522145529-93d6bda14341 github.com/openshift/operator-custom-metrics v0.5.0 - github.com/openshift/osde2e-common v0.0.0-20240604133256-b7200cad0cca github.com/operator-framework/operator-lib v0.11.0 github.com/prometheus/client_golang v1.22.0 github.com/redhat-cop/operator-utils v1.3.7 @@ -46,7 +45,6 @@ require ( github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -54,12 +52,10 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect - github.com/moby/spdystream v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.55.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -93,7 +89,6 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kubectl v0.28.2 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - sigs.k8s.io/e2e-framework v0.3.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect diff --git a/go.sum b/go.sum index a3a20dc20..383543b8d 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,6 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -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/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= @@ -92,8 +90,6 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= -github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -122,8 +118,6 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= -github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -133,8 +127,6 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -147,8 +139,6 @@ github.com/openshift/api v0.0.0-20240522145529-93d6bda14341 h1:JQpzgk+p24rkgNbNs github.com/openshift/api v0.0.0-20240522145529-93d6bda14341/go.mod h1:qNtV0315F+f8ld52TLtPvrfivZpdimOzTi3kn9IVbtU= github.com/openshift/operator-custom-metrics v0.5.0 h1:iRZ6e3HvGSxw1dZgnY5emEN6fg9u/OhHfEC+u3ovhSE= github.com/openshift/operator-custom-metrics v0.5.0/go.mod h1:garCvCZK9l0RS0vCcz5anKsJSU60XgizYXllbMjRW5M= -github.com/openshift/osde2e-common v0.0.0-20240604133256-b7200cad0cca h1:Sy0D7WuQ2zzzgOsjpyMXywY6Yl5iLj8CO9jLZvtDbC0= -github.com/openshift/osde2e-common v0.0.0-20240604133256-b7200cad0cca/go.mod h1:oA2+sZPgApVQHbLLWFInDMzheCovGpnWg9ABXppFQpQ= github.com/operator-framework/operator-lib v0.11.0 h1:eYzqpiOfq9WBI4Trddisiq/X9BwCisZd3rIzmHRC9Z8= github.com/operator-framework/operator-lib v0.11.0/go.mod h1:RpyKhFAoG6DmKTDIwMuO6pI3LRc8IE9rxEYWy476o6g= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -192,8 +182,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY= -github.com/vladimirvivien/gexe v0.2.0/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= @@ -346,8 +334,6 @@ k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6J k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/e2e-framework v0.3.0 h1:eqQALBtPCth8+ulTs6lcPK7ytV5rZSSHJzQHZph4O7U= -sigs.k8s.io/e2e-framework v0.3.0/go.mod h1:C+ef37/D90Dc7Xq1jQnNbJYscrUGpxrWog9bx2KIa+c= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore deleted file mode 100644 index cd3fcd1ef..000000000 --- a/vendor/github.com/gorilla/websocket/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -.idea/ -*.iml diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS deleted file mode 100644 index 1931f4006..000000000 --- a/vendor/github.com/gorilla/websocket/AUTHORS +++ /dev/null @@ -1,9 +0,0 @@ -# This is the official list of Gorilla WebSocket authors for copyright -# purposes. -# -# Please keep the list sorted. - -Gary Burd -Google LLC (https://opensource.google.com/) -Joachim Bauch - diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE deleted file mode 100644 index 9171c9722..000000000 --- a/vendor/github.com/gorilla/websocket/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md deleted file mode 100644 index ff8bfab0b..000000000 --- a/vendor/github.com/gorilla/websocket/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Gorilla WebSocket - -[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) -[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket) - -Gorilla WebSocket is a [Go](http://golang.org/) implementation of the -[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. - - -### Documentation - -* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) -* [Chat example](https://github.com/gorilla/websocket/tree/main/examples/chat) -* [Command example](https://github.com/gorilla/websocket/tree/main/examples/command) -* [Client and server example](https://github.com/gorilla/websocket/tree/main/examples/echo) -* [File watch example](https://github.com/gorilla/websocket/tree/main/examples/filewatch) - -### Status - -The Gorilla WebSocket package provides a complete and tested implementation of -the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The -package API is stable. - -### Installation - - go get github.com/gorilla/websocket - -### Protocol Compliance - -The Gorilla WebSocket package passes the server tests in the [Autobahn Test -Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn -subdirectory](https://github.com/gorilla/websocket/tree/main/examples/autobahn). diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go deleted file mode 100644 index 00917ea34..000000000 --- a/vendor/github.com/gorilla/websocket/client.go +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bytes" - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/http/httptrace" - "net/url" - "strings" - "time" -) - -// ErrBadHandshake is returned when the server response to opening handshake is -// invalid. -var ErrBadHandshake = errors.New("websocket: bad handshake") - -var errInvalidCompression = errors.New("websocket: invalid compression negotiation") - -// NewClient creates a new client connection using the given net connection. -// The URL u specifies the host and request URI. Use requestHeader to specify -// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies -// (Cookie). Use the response.Header to get the selected subprotocol -// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). -// -// If the WebSocket handshake fails, ErrBadHandshake is returned along with a -// non-nil *http.Response so that callers can handle redirects, authentication, -// etc. -// -// Deprecated: Use Dialer instead. -func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { - d := Dialer{ - ReadBufferSize: readBufSize, - WriteBufferSize: writeBufSize, - NetDial: func(net, addr string) (net.Conn, error) { - return netConn, nil - }, - } - return d.Dial(u.String(), requestHeader) -} - -// A Dialer contains options for connecting to WebSocket server. -// -// It is safe to call Dialer's methods concurrently. -type Dialer struct { - // The following custom dial functions can be set to establish - // connections to either the backend server or the proxy (if it - // exists). The scheme of the dialed entity (either backend or - // proxy) determines which custom dial function is selected: - // either NetDialTLSContext for HTTPS or NetDialContext/NetDial - // for HTTP. Since the "Proxy" function can determine the scheme - // dynamically, it can make sense to set multiple custom dial - // functions simultaneously. - // - // NetDial specifies the dial function for creating TCP connections. If - // NetDial is nil, net.Dialer DialContext is used. - // If "Proxy" field is also set, this function dials the proxy--not - // the backend server. - NetDial func(network, addr string) (net.Conn, error) - - // NetDialContext specifies the dial function for creating TCP connections. If - // NetDialContext is nil, NetDial is used. - // If "Proxy" field is also set, this function dials the proxy--not - // the backend server. - NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error) - - // NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If - // NetDialTLSContext is nil, NetDialContext is used. - // If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and - // TLSClientConfig is ignored. - // If "Proxy" field is also set, this function dials the proxy (and performs - // the TLS handshake with the proxy, ignoring TLSClientConfig). In this TLS proxy - // dialing case the TLSClientConfig could still be necessary for TLS to the backend server. - NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error) - - // Proxy specifies a function to return a proxy for a given - // Request. If the function returns a non-nil error, the - // request is aborted with the provided error. - // If Proxy is nil or returns a nil *URL, no proxy is used. - Proxy func(*http.Request) (*url.URL, error) - - // TLSClientConfig specifies the TLS configuration to use with tls.Client. - // If nil, the default configuration is used. - // If NetDialTLSContext is set, Dial assumes the TLS handshake - // is done there and TLSClientConfig is ignored. - TLSClientConfig *tls.Config - - // HandshakeTimeout specifies the duration for the handshake to complete. - HandshakeTimeout time.Duration - - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer - // size is zero, then a useful default size is used. The I/O buffer sizes - // do not limit the size of the messages that can be sent or received. - ReadBufferSize, WriteBufferSize int - - // WriteBufferPool is a pool of buffers for write operations. If the value - // is not set, then write buffers are allocated to the connection for the - // lifetime of the connection. - // - // A pool is most useful when the application has a modest volume of writes - // across a large number of connections. - // - // Applications should use a single pool for each unique value of - // WriteBufferSize. - WriteBufferPool BufferPool - - // Subprotocols specifies the client's requested subprotocols. - Subprotocols []string - - // EnableCompression specifies if the client should attempt to negotiate - // per message compression (RFC 7692). Setting this value to true does not - // guarantee that compression will be supported. Currently only "no context - // takeover" modes are supported. - EnableCompression bool - - // Jar specifies the cookie jar. - // If Jar is nil, cookies are not sent in requests and ignored - // in responses. - Jar http.CookieJar -} - -// Dial creates a new client connection by calling DialContext with a background context. -func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { - return d.DialContext(context.Background(), urlStr, requestHeader) -} - -var errMalformedURL = errors.New("malformed ws or wss URL") - -func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { - hostPort = u.Host - hostNoPort = u.Host - if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { - hostNoPort = hostNoPort[:i] - } else { - switch u.Scheme { - case "wss": - hostPort += ":443" - case "https": - hostPort += ":443" - default: - hostPort += ":80" - } - } - return hostPort, hostNoPort -} - -// DefaultDialer is a dialer with all fields set to the default values. -var DefaultDialer = &Dialer{ - Proxy: http.ProxyFromEnvironment, - HandshakeTimeout: 45 * time.Second, -} - -// nilDialer is dialer to use when receiver is nil. -var nilDialer = *DefaultDialer - -// DialContext creates a new client connection. Use requestHeader to specify the -// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). -// Use the response.Header to get the selected subprotocol -// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). -// -// The context will be used in the request and in the Dialer. -// -// If the WebSocket handshake fails, ErrBadHandshake is returned along with a -// non-nil *http.Response so that callers can handle redirects, authentication, -// etcetera. The response body may not contain the entire response and does not -// need to be closed by the application. -func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { - if d == nil { - d = &nilDialer - } - - challengeKey, err := generateChallengeKey() - if err != nil { - return nil, nil, err - } - - u, err := url.Parse(urlStr) - if err != nil { - return nil, nil, err - } - - switch u.Scheme { - case "ws": - u.Scheme = "http" - case "wss": - u.Scheme = "https" - default: - return nil, nil, errMalformedURL - } - - if u.User != nil { - // User name and password are not allowed in websocket URIs. - return nil, nil, errMalformedURL - } - - req := &http.Request{ - Method: http.MethodGet, - URL: u, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), - Host: u.Host, - } - req = req.WithContext(ctx) - - // Set the cookies present in the cookie jar of the dialer - if d.Jar != nil { - for _, cookie := range d.Jar.Cookies(u) { - req.AddCookie(cookie) - } - } - - // Set the request headers using the capitalization for names and values in - // RFC examples. Although the capitalization shouldn't matter, there are - // servers that depend on it. The Header.Set method is not used because the - // method canonicalizes the header names. - req.Header["Upgrade"] = []string{"websocket"} - req.Header["Connection"] = []string{"Upgrade"} - req.Header["Sec-WebSocket-Key"] = []string{challengeKey} - req.Header["Sec-WebSocket-Version"] = []string{"13"} - if len(d.Subprotocols) > 0 { - req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} - } - for k, vs := range requestHeader { - switch { - case k == "Host": - if len(vs) > 0 { - req.Host = vs[0] - } - case k == "Upgrade" || - k == "Connection" || - k == "Sec-Websocket-Key" || - k == "Sec-Websocket-Version" || - k == "Sec-Websocket-Extensions" || - (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): - return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) - case k == "Sec-Websocket-Protocol": - req.Header["Sec-WebSocket-Protocol"] = vs - default: - req.Header[k] = vs - } - } - - if d.EnableCompression { - req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"} - } - - if d.HandshakeTimeout != 0 { - var cancel func() - ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout) - defer cancel() - } - - var proxyURL *url.URL - if d.Proxy != nil { - proxyURL, err = d.Proxy(req) - if err != nil { - return nil, nil, err - } - } - netDial, err := d.netDialFn(ctx, proxyURL, u) - if err != nil { - return nil, nil, err - } - - hostPort, hostNoPort := hostPortNoPort(u) - trace := httptrace.ContextClientTrace(ctx) - if trace != nil && trace.GetConn != nil { - trace.GetConn(hostPort) - } - - netConn, err := netDial(ctx, "tcp", hostPort) - if err != nil { - return nil, nil, err - } - if trace != nil && trace.GotConn != nil { - trace.GotConn(httptrace.GotConnInfo{ - Conn: netConn, - }) - } - - // Close the network connection when returning an error. The variable - // netConn is set to nil before the success return at the end of the - // function. - defer func() { - if netConn != nil { - // It's safe to ignore the error from Close() because this code is - // only executed when returning a more important error to the - // application. - _ = netConn.Close() - } - }() - - // Do TLS handshake over established connection if a proxy exists. - if proxyURL != nil && u.Scheme == "https" { - - cfg := cloneTLSConfig(d.TLSClientConfig) - if cfg.ServerName == "" { - cfg.ServerName = hostNoPort - } - tlsConn := tls.Client(netConn, cfg) - netConn = tlsConn - - if trace != nil && trace.TLSHandshakeStart != nil { - trace.TLSHandshakeStart() - } - err := doHandshake(ctx, tlsConn, cfg) - if trace != nil && trace.TLSHandshakeDone != nil { - trace.TLSHandshakeDone(tlsConn.ConnectionState(), err) - } - - if err != nil { - return nil, nil, err - } - } - - conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil) - - if err := req.Write(netConn); err != nil { - return nil, nil, err - } - - if trace != nil && trace.GotFirstResponseByte != nil { - if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 { - trace.GotFirstResponseByte() - } - } - - resp, err := http.ReadResponse(conn.br, req) - if err != nil { - if d.TLSClientConfig != nil { - for _, proto := range d.TLSClientConfig.NextProtos { - if proto != "http/1.1" { - return nil, nil, fmt.Errorf( - "websocket: protocol %q was given but is not supported;"+ - "sharing tls.Config with net/http Transport can cause this error: %w", - proto, err, - ) - } - } - } - return nil, nil, err - } - - if d.Jar != nil { - if rc := resp.Cookies(); len(rc) > 0 { - d.Jar.SetCookies(u, rc) - } - } - - if resp.StatusCode != 101 || - !tokenListContainsValue(resp.Header, "Upgrade", "websocket") || - !tokenListContainsValue(resp.Header, "Connection", "upgrade") || - resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { - // Before closing the network connection on return from this - // function, slurp up some of the response to aid application - // debugging. - buf := make([]byte, 1024) - n, _ := io.ReadFull(resp.Body, buf) - resp.Body = io.NopCloser(bytes.NewReader(buf[:n])) - return nil, resp, ErrBadHandshake - } - - for _, ext := range parseExtensions(resp.Header) { - if ext[""] != "permessage-deflate" { - continue - } - _, snct := ext["server_no_context_takeover"] - _, cnct := ext["client_no_context_takeover"] - if !snct || !cnct { - return nil, resp, errInvalidCompression - } - conn.newCompressionWriter = compressNoContextTakeover - conn.newDecompressionReader = decompressNoContextTakeover - break - } - - resp.Body = io.NopCloser(bytes.NewReader([]byte{})) - conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") - - if err := netConn.SetDeadline(time.Time{}); err != nil { - return nil, resp, err - } - - // Success! Set netConn to nil to stop the deferred function above from - // closing the network connection. - netConn = nil - - return conn, resp, nil -} - -// Returns the dial function to establish the connection to either the backend -// server or the proxy (if it exists). If the dialed entity is HTTPS, then the -// returned dial function *also* performs the TLS handshake to the dialed entity. -// NOTE: If a proxy exists, it is possible for a second TLS handshake to be -// necessary over the established connection. -func (d *Dialer) netDialFn(ctx context.Context, proxyURL *url.URL, backendURL *url.URL) (netDialerFunc, error) { - var netDial netDialerFunc - if proxyURL != nil { - netDial = d.netDialFromURL(proxyURL) - } else { - netDial = d.netDialFromURL(backendURL) - } - // If needed, wrap the dial function to set the connection deadline. - if deadline, ok := ctx.Deadline(); ok { - netDial = netDialWithDeadline(netDial, deadline) - } - // Proxy dialing is wrapped to implement CONNECT method and possibly proxy auth. - if proxyURL != nil { - return proxyFromURL(proxyURL, netDial) - } - return netDial, nil -} - -// Returns function to create the connection depending on the Dialer's -// custom dialing functions and the passed URL of entity connecting to. -func (d *Dialer) netDialFromURL(u *url.URL) netDialerFunc { - var netDial netDialerFunc - switch { - case d.NetDialContext != nil: - netDial = d.NetDialContext - case d.NetDial != nil: - netDial = func(ctx context.Context, net, addr string) (net.Conn, error) { - return d.NetDial(net, addr) - } - default: - netDial = (&net.Dialer{}).DialContext - } - // If dialed entity is HTTPS, then either use custom TLS dialing function (if exists) - // or wrap the previously computed "netDial" to use TLS config for handshake. - if u.Scheme == "https" { - if d.NetDialTLSContext != nil { - netDial = d.NetDialTLSContext - } else { - netDial = netDialWithTLSHandshake(netDial, d.TLSClientConfig, u) - } - } - return netDial -} - -// Returns wrapped "netDial" function, performing TLS handshake after connecting. -func netDialWithTLSHandshake(netDial netDialerFunc, tlsConfig *tls.Config, u *url.URL) netDialerFunc { - return func(ctx context.Context, unused, addr string) (net.Conn, error) { - hostPort, hostNoPort := hostPortNoPort(u) - trace := httptrace.ContextClientTrace(ctx) - if trace != nil && trace.GetConn != nil { - trace.GetConn(hostPort) - } - // Creates TCP connection to addr using passed "netDial" function. - conn, err := netDial(ctx, "tcp", addr) - if err != nil { - return nil, err - } - cfg := cloneTLSConfig(tlsConfig) - if cfg.ServerName == "" { - cfg.ServerName = hostNoPort - } - tlsConn := tls.Client(conn, cfg) - // Do the TLS handshake using TLSConfig over the wrapped connection. - if trace != nil && trace.TLSHandshakeStart != nil { - trace.TLSHandshakeStart() - } - err = doHandshake(ctx, tlsConn, cfg) - if trace != nil && trace.TLSHandshakeDone != nil { - trace.TLSHandshakeDone(tlsConn.ConnectionState(), err) - } - if err != nil { - tlsConn.Close() - return nil, err - } - return tlsConn, nil - } -} - -// Returns wrapped "netDial" function, setting passed deadline. -func netDialWithDeadline(netDial netDialerFunc, deadline time.Time) netDialerFunc { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - c, err := netDial(ctx, network, addr) - if err != nil { - return nil, err - } - err = c.SetDeadline(deadline) - if err != nil { - c.Close() - return nil, err - } - return c, nil - } -} - -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return cfg.Clone() -} - -func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error { - if err := tlsConn.HandshakeContext(ctx); err != nil { - return err - } - if !cfg.InsecureSkipVerify { - if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { - return err - } - } - return nil -} diff --git a/vendor/github.com/gorilla/websocket/compression.go b/vendor/github.com/gorilla/websocket/compression.go deleted file mode 100644 index fe1079edb..000000000 --- a/vendor/github.com/gorilla/websocket/compression.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "compress/flate" - "errors" - "io" - "strings" - "sync" -) - -const ( - minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 - maxCompressionLevel = flate.BestCompression - defaultCompressionLevel = 1 -) - -var ( - flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool - flateReaderPool = sync.Pool{New: func() interface{} { - return flate.NewReader(nil) - }} -) - -func decompressNoContextTakeover(r io.Reader) io.ReadCloser { - const tail = - // Add four bytes as specified in RFC - "\x00\x00\xff\xff" + - // Add final block to squelch unexpected EOF error from flate reader. - "\x01\x00\x00\xff\xff" - - fr, _ := flateReaderPool.Get().(io.ReadCloser) - mr := io.MultiReader(r, strings.NewReader(tail)) - if err := fr.(flate.Resetter).Reset(mr, nil); err != nil { - // Reset never fails, but handle error in case that changes. - fr = flate.NewReader(mr) - } - return &flateReadWrapper{fr} -} - -func isValidCompressionLevel(level int) bool { - return minCompressionLevel <= level && level <= maxCompressionLevel -} - -func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { - p := &flateWriterPools[level-minCompressionLevel] - tw := &truncWriter{w: w} - fw, _ := p.Get().(*flate.Writer) - if fw == nil { - fw, _ = flate.NewWriter(tw, level) - } else { - fw.Reset(tw) - } - return &flateWriteWrapper{fw: fw, tw: tw, p: p} -} - -// truncWriter is an io.Writer that writes all but the last four bytes of the -// stream to another io.Writer. -type truncWriter struct { - w io.WriteCloser - n int - p [4]byte -} - -func (w *truncWriter) Write(p []byte) (int, error) { - n := 0 - - // fill buffer first for simplicity. - if w.n < len(w.p) { - n = copy(w.p[w.n:], p) - p = p[n:] - w.n += n - if len(p) == 0 { - return n, nil - } - } - - m := len(p) - if m > len(w.p) { - m = len(w.p) - } - - if nn, err := w.w.Write(w.p[:m]); err != nil { - return n + nn, err - } - - copy(w.p[:], w.p[m:]) - copy(w.p[len(w.p)-m:], p[len(p)-m:]) - nn, err := w.w.Write(p[:len(p)-m]) - return n + nn, err -} - -type flateWriteWrapper struct { - fw *flate.Writer - tw *truncWriter - p *sync.Pool -} - -func (w *flateWriteWrapper) Write(p []byte) (int, error) { - if w.fw == nil { - return 0, errWriteClosed - } - return w.fw.Write(p) -} - -func (w *flateWriteWrapper) Close() error { - if w.fw == nil { - return errWriteClosed - } - err1 := w.fw.Flush() - w.p.Put(w.fw) - w.fw = nil - if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { - return errors.New("websocket: internal error, unexpected bytes at end of flate stream") - } - err2 := w.tw.w.Close() - if err1 != nil { - return err1 - } - return err2 -} - -type flateReadWrapper struct { - fr io.ReadCloser -} - -func (r *flateReadWrapper) Read(p []byte) (int, error) { - if r.fr == nil { - return 0, io.ErrClosedPipe - } - n, err := r.fr.Read(p) - if err == io.EOF { - // Preemptively place the reader back in the pool. This helps with - // scenarios where the application does not call NextReader() soon after - // this final read. - r.Close() - } - return n, err -} - -func (r *flateReadWrapper) Close() error { - if r.fr == nil { - return io.ErrClosedPipe - } - err := r.fr.Close() - flateReaderPool.Put(r.fr) - r.fr = nil - return err -} diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go deleted file mode 100644 index 9562ffd49..000000000 --- a/vendor/github.com/gorilla/websocket/conn.go +++ /dev/null @@ -1,1246 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "crypto/rand" - "encoding/binary" - "errors" - "io" - "net" - "strconv" - "strings" - "sync" - "time" - "unicode/utf8" -) - -const ( - // Frame header byte 0 bits from Section 5.2 of RFC 6455 - finalBit = 1 << 7 - rsv1Bit = 1 << 6 - rsv2Bit = 1 << 5 - rsv3Bit = 1 << 4 - - // Frame header byte 1 bits from Section 5.2 of RFC 6455 - maskBit = 1 << 7 - - maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask - maxControlFramePayloadSize = 125 - - writeWait = time.Second - - defaultReadBufferSize = 4096 - defaultWriteBufferSize = 4096 - - continuationFrame = 0 - noFrame = -1 -) - -// Close codes defined in RFC 6455, section 11.7. -const ( - CloseNormalClosure = 1000 - CloseGoingAway = 1001 - CloseProtocolError = 1002 - CloseUnsupportedData = 1003 - CloseNoStatusReceived = 1005 - CloseAbnormalClosure = 1006 - CloseInvalidFramePayloadData = 1007 - ClosePolicyViolation = 1008 - CloseMessageTooBig = 1009 - CloseMandatoryExtension = 1010 - CloseInternalServerErr = 1011 - CloseServiceRestart = 1012 - CloseTryAgainLater = 1013 - CloseTLSHandshake = 1015 -) - -// The message types are defined in RFC 6455, section 11.8. -const ( - // TextMessage denotes a text data message. The text message payload is - // interpreted as UTF-8 encoded text data. - TextMessage = 1 - - // BinaryMessage denotes a binary data message. - BinaryMessage = 2 - - // CloseMessage denotes a close control message. The optional message - // payload contains a numeric code and text. Use the FormatCloseMessage - // function to format a close message payload. - CloseMessage = 8 - - // PingMessage denotes a ping control message. The optional message payload - // is UTF-8 encoded text. - PingMessage = 9 - - // PongMessage denotes a pong control message. The optional message payload - // is UTF-8 encoded text. - PongMessage = 10 -) - -// ErrCloseSent is returned when the application writes a message to the -// connection after sending a close message. -var ErrCloseSent = errors.New("websocket: close sent") - -// ErrReadLimit is returned when reading a message that is larger than the -// read limit set for the connection. -var ErrReadLimit = errors.New("websocket: read limit exceeded") - -// netError satisfies the net Error interface. -type netError struct { - msg string - temporary bool - timeout bool -} - -func (e *netError) Error() string { return e.msg } -func (e *netError) Temporary() bool { return e.temporary } -func (e *netError) Timeout() bool { return e.timeout } - -// CloseError represents a close message. -type CloseError struct { - // Code is defined in RFC 6455, section 11.7. - Code int - - // Text is the optional text payload. - Text string -} - -func (e *CloseError) Error() string { - s := []byte("websocket: close ") - s = strconv.AppendInt(s, int64(e.Code), 10) - switch e.Code { - case CloseNormalClosure: - s = append(s, " (normal)"...) - case CloseGoingAway: - s = append(s, " (going away)"...) - case CloseProtocolError: - s = append(s, " (protocol error)"...) - case CloseUnsupportedData: - s = append(s, " (unsupported data)"...) - case CloseNoStatusReceived: - s = append(s, " (no status)"...) - case CloseAbnormalClosure: - s = append(s, " (abnormal closure)"...) - case CloseInvalidFramePayloadData: - s = append(s, " (invalid payload data)"...) - case ClosePolicyViolation: - s = append(s, " (policy violation)"...) - case CloseMessageTooBig: - s = append(s, " (message too big)"...) - case CloseMandatoryExtension: - s = append(s, " (mandatory extension missing)"...) - case CloseInternalServerErr: - s = append(s, " (internal server error)"...) - case CloseTLSHandshake: - s = append(s, " (TLS handshake error)"...) - } - if e.Text != "" { - s = append(s, ": "...) - s = append(s, e.Text...) - } - return string(s) -} - -// IsCloseError returns boolean indicating whether the error is a *CloseError -// with one of the specified codes. -func IsCloseError(err error, codes ...int) bool { - if e, ok := err.(*CloseError); ok { - for _, code := range codes { - if e.Code == code { - return true - } - } - } - return false -} - -// IsUnexpectedCloseError returns boolean indicating whether the error is a -// *CloseError with a code not in the list of expected codes. -func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { - if e, ok := err.(*CloseError); ok { - for _, code := range expectedCodes { - if e.Code == code { - return false - } - } - return true - } - return false -} - -var ( - errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} - errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} - errBadWriteOpCode = errors.New("websocket: bad write message type") - errWriteClosed = errors.New("websocket: write closed") - errInvalidControlFrame = errors.New("websocket: invalid control frame") -) - -// maskRand is an io.Reader for generating mask bytes. The reader is initialized -// to crypto/rand Reader. Tests swap the reader to a math/rand reader for -// reproducible results. -var maskRand = rand.Reader - -// newMaskKey returns a new 32 bit value for masking client frames. -func newMaskKey() [4]byte { - var k [4]byte - _, _ = io.ReadFull(maskRand, k[:]) - return k -} - -func isControl(frameType int) bool { - return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage -} - -func isData(frameType int) bool { - return frameType == TextMessage || frameType == BinaryMessage -} - -var validReceivedCloseCodes = map[int]bool{ - // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number - - CloseNormalClosure: true, - CloseGoingAway: true, - CloseProtocolError: true, - CloseUnsupportedData: true, - CloseNoStatusReceived: false, - CloseAbnormalClosure: false, - CloseInvalidFramePayloadData: true, - ClosePolicyViolation: true, - CloseMessageTooBig: true, - CloseMandatoryExtension: true, - CloseInternalServerErr: true, - CloseServiceRestart: true, - CloseTryAgainLater: true, - CloseTLSHandshake: false, -} - -func isValidReceivedCloseCode(code int) bool { - return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) -} - -// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this -// interface. The type of the value stored in a pool is not specified. -type BufferPool interface { - // Get gets a value from the pool or returns nil if the pool is empty. - Get() interface{} - // Put adds a value to the pool. - Put(interface{}) -} - -// writePoolData is the type added to the write buffer pool. This wrapper is -// used to prevent applications from peeking at and depending on the values -// added to the pool. -type writePoolData struct{ buf []byte } - -// The Conn type represents a WebSocket connection. -type Conn struct { - conn net.Conn - isServer bool - subprotocol string - - // Write fields - mu chan struct{} // used as mutex to protect write to conn - writeBuf []byte // frame is constructed in this buffer. - writePool BufferPool - writeBufSize int - writeDeadline time.Time - writer io.WriteCloser // the current writer returned to the application - isWriting bool // for best-effort concurrent write detection - - writeErrMu sync.Mutex - writeErr error - - enableWriteCompression bool - compressionLevel int - newCompressionWriter func(io.WriteCloser, int) io.WriteCloser - - // Read fields - reader io.ReadCloser // the current reader returned to the application - readErr error - br *bufio.Reader - // bytes remaining in current frame. - // set setReadRemaining to safely update this value and prevent overflow - readRemaining int64 - readFinal bool // true the current message has more frames. - readLength int64 // Message size. - readLimit int64 // Maximum message size. - readMaskPos int - readMaskKey [4]byte - handlePong func(string) error - handlePing func(string) error - handleClose func(int, string) error - readErrCount int - messageReader *messageReader // the current low-level reader - - readDecompress bool // whether last read frame had RSV1 set - newDecompressionReader func(io.Reader) io.ReadCloser -} - -func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn { - - if br == nil { - if readBufferSize == 0 { - readBufferSize = defaultReadBufferSize - } else if readBufferSize < maxControlFramePayloadSize { - // must be large enough for control frame - readBufferSize = maxControlFramePayloadSize - } - br = bufio.NewReaderSize(conn, readBufferSize) - } - - if writeBufferSize <= 0 { - writeBufferSize = defaultWriteBufferSize - } - writeBufferSize += maxFrameHeaderSize - - if writeBuf == nil && writeBufferPool == nil { - writeBuf = make([]byte, writeBufferSize) - } - - mu := make(chan struct{}, 1) - mu <- struct{}{} - c := &Conn{ - isServer: isServer, - br: br, - conn: conn, - mu: mu, - readFinal: true, - writeBuf: writeBuf, - writePool: writeBufferPool, - writeBufSize: writeBufferSize, - enableWriteCompression: true, - compressionLevel: defaultCompressionLevel, - } - c.SetCloseHandler(nil) - c.SetPingHandler(nil) - c.SetPongHandler(nil) - return c -} - -// setReadRemaining tracks the number of bytes remaining on the connection. If n -// overflows, an ErrReadLimit is returned. -func (c *Conn) setReadRemaining(n int64) error { - if n < 0 { - return ErrReadLimit - } - - c.readRemaining = n - return nil -} - -// Subprotocol returns the negotiated protocol for the connection. -func (c *Conn) Subprotocol() string { - return c.subprotocol -} - -// Close closes the underlying network connection without sending or waiting -// for a close message. -func (c *Conn) Close() error { - return c.conn.Close() -} - -// LocalAddr returns the local network address. -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -// RemoteAddr returns the remote network address. -func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -// Write methods - -func (c *Conn) writeFatal(err error) error { - c.writeErrMu.Lock() - if c.writeErr == nil { - c.writeErr = err - } - c.writeErrMu.Unlock() - return err -} - -func (c *Conn) read(n int) ([]byte, error) { - p, err := c.br.Peek(n) - if err == io.EOF { - err = errUnexpectedEOF - } - // Discard is guaranteed to succeed because the number of bytes to discard - // is less than or equal to the number of bytes buffered. - _, _ = c.br.Discard(len(p)) - return p, err -} - -func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { - <-c.mu - defer func() { c.mu <- struct{}{} }() - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - if err := c.conn.SetWriteDeadline(deadline); err != nil { - return c.writeFatal(err) - } - if len(buf1) == 0 { - _, err = c.conn.Write(buf0) - } else { - err = c.writeBufs(buf0, buf1) - } - if err != nil { - return c.writeFatal(err) - } - if frameType == CloseMessage { - _ = c.writeFatal(ErrCloseSent) - } - return nil -} - -func (c *Conn) writeBufs(bufs ...[]byte) error { - b := net.Buffers(bufs) - _, err := b.WriteTo(c.conn) - return err -} - -// WriteControl writes a control message with the given deadline. The allowed -// message types are CloseMessage, PingMessage and PongMessage. -func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { - if !isControl(messageType) { - return errBadWriteOpCode - } - if len(data) > maxControlFramePayloadSize { - return errInvalidControlFrame - } - - b0 := byte(messageType) | finalBit - b1 := byte(len(data)) - if !c.isServer { - b1 |= maskBit - } - - buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) - buf = append(buf, b0, b1) - - if c.isServer { - buf = append(buf, data...) - } else { - key := newMaskKey() - buf = append(buf, key[:]...) - buf = append(buf, data...) - maskBytes(key, 0, buf[6:]) - } - - if deadline.IsZero() { - // No timeout for zero time. - <-c.mu - } else { - d := time.Until(deadline) - if d < 0 { - return errWriteTimeout - } - select { - case <-c.mu: - default: - timer := time.NewTimer(d) - select { - case <-c.mu: - timer.Stop() - case <-timer.C: - return errWriteTimeout - } - } - } - - defer func() { c.mu <- struct{}{} }() - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - if err := c.conn.SetWriteDeadline(deadline); err != nil { - return c.writeFatal(err) - } - if _, err = c.conn.Write(buf); err != nil { - return c.writeFatal(err) - } - if messageType == CloseMessage { - _ = c.writeFatal(ErrCloseSent) - } - return err -} - -// beginMessage prepares a connection and message writer for a new message. -func (c *Conn) beginMessage(mw *messageWriter, messageType int) error { - // Close previous writer if not already closed by the application. It's - // probably better to return an error in this situation, but we cannot - // change this without breaking existing applications. - if c.writer != nil { - c.writer.Close() - c.writer = nil - } - - if !isControl(messageType) && !isData(messageType) { - return errBadWriteOpCode - } - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - mw.c = c - mw.frameType = messageType - mw.pos = maxFrameHeaderSize - - if c.writeBuf == nil { - wpd, ok := c.writePool.Get().(writePoolData) - if ok { - c.writeBuf = wpd.buf - } else { - c.writeBuf = make([]byte, c.writeBufSize) - } - } - return nil -} - -// NextWriter returns a writer for the next message to send. The writer's Close -// method flushes the complete message to the network. -// -// There can be at most one open writer on a connection. NextWriter closes the -// previous writer if the application has not already done so. -// -// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and -// PongMessage) are supported. -func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { - var mw messageWriter - if err := c.beginMessage(&mw, messageType); err != nil { - return nil, err - } - c.writer = &mw - if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { - w := c.newCompressionWriter(c.writer, c.compressionLevel) - mw.compress = true - c.writer = w - } - return c.writer, nil -} - -type messageWriter struct { - c *Conn - compress bool // whether next call to flushFrame should set RSV1 - pos int // end of data in writeBuf. - frameType int // type of the current frame. - err error -} - -func (w *messageWriter) endMessage(err error) error { - if w.err != nil { - return err - } - c := w.c - w.err = err - c.writer = nil - if c.writePool != nil { - c.writePool.Put(writePoolData{buf: c.writeBuf}) - c.writeBuf = nil - } - return err -} - -// flushFrame writes buffered data and extra as a frame to the network. The -// final argument indicates that this is the last frame in the message. -func (w *messageWriter) flushFrame(final bool, extra []byte) error { - c := w.c - length := w.pos - maxFrameHeaderSize + len(extra) - - // Check for invalid control frames. - if isControl(w.frameType) && - (!final || length > maxControlFramePayloadSize) { - return w.endMessage(errInvalidControlFrame) - } - - b0 := byte(w.frameType) - if final { - b0 |= finalBit - } - if w.compress { - b0 |= rsv1Bit - } - w.compress = false - - b1 := byte(0) - if !c.isServer { - b1 |= maskBit - } - - // Assume that the frame starts at beginning of c.writeBuf. - framePos := 0 - if c.isServer { - // Adjust up if mask not included in the header. - framePos = 4 - } - - switch { - case length >= 65536: - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | 127 - binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) - case length > 125: - framePos += 6 - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | 126 - binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) - default: - framePos += 8 - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | byte(length) - } - - if !c.isServer { - key := newMaskKey() - copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) - maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) - if len(extra) > 0 { - return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))) - } - } - - // Write the buffers to the connection with best-effort detection of - // concurrent writes. See the concurrency section in the package - // documentation for more info. - - if c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = true - - err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) - - if !c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = false - - if err != nil { - return w.endMessage(err) - } - - if final { - _ = w.endMessage(errWriteClosed) - return nil - } - - // Setup for next frame. - w.pos = maxFrameHeaderSize - w.frameType = continuationFrame - return nil -} - -func (w *messageWriter) ncopy(max int) (int, error) { - n := len(w.c.writeBuf) - w.pos - if n <= 0 { - if err := w.flushFrame(false, nil); err != nil { - return 0, err - } - n = len(w.c.writeBuf) - w.pos - } - if n > max { - n = max - } - return n, nil -} - -func (w *messageWriter) Write(p []byte) (int, error) { - if w.err != nil { - return 0, w.err - } - - if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { - // Don't buffer large messages. - err := w.flushFrame(false, p) - if err != nil { - return 0, err - } - return len(p), nil - } - - nn := len(p) - for len(p) > 0 { - n, err := w.ncopy(len(p)) - if err != nil { - return 0, err - } - copy(w.c.writeBuf[w.pos:], p[:n]) - w.pos += n - p = p[n:] - } - return nn, nil -} - -func (w *messageWriter) WriteString(p string) (int, error) { - if w.err != nil { - return 0, w.err - } - - nn := len(p) - for len(p) > 0 { - n, err := w.ncopy(len(p)) - if err != nil { - return 0, err - } - copy(w.c.writeBuf[w.pos:], p[:n]) - w.pos += n - p = p[n:] - } - return nn, nil -} - -func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { - if w.err != nil { - return 0, w.err - } - for { - if w.pos == len(w.c.writeBuf) { - err = w.flushFrame(false, nil) - if err != nil { - break - } - } - var n int - n, err = r.Read(w.c.writeBuf[w.pos:]) - w.pos += n - nn += int64(n) - if err != nil { - if err == io.EOF { - err = nil - } - break - } - } - return nn, err -} - -func (w *messageWriter) Close() error { - if w.err != nil { - return w.err - } - return w.flushFrame(true, nil) -} - -// WritePreparedMessage writes prepared message into connection. -func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { - frameType, frameData, err := pm.frame(prepareKey{ - isServer: c.isServer, - compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), - compressionLevel: c.compressionLevel, - }) - if err != nil { - return err - } - if c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = true - err = c.write(frameType, c.writeDeadline, frameData, nil) - if !c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = false - return err -} - -// WriteMessage is a helper method for getting a writer using NextWriter, -// writing the message and closing the writer. -func (c *Conn) WriteMessage(messageType int, data []byte) error { - - if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { - // Fast path with no allocations and single frame. - - var mw messageWriter - if err := c.beginMessage(&mw, messageType); err != nil { - return err - } - n := copy(c.writeBuf[mw.pos:], data) - mw.pos += n - data = data[n:] - return mw.flushFrame(true, data) - } - - w, err := c.NextWriter(messageType) - if err != nil { - return err - } - if _, err = w.Write(data); err != nil { - return err - } - return w.Close() -} - -// SetWriteDeadline sets the write deadline on the underlying network -// connection. After a write has timed out, the websocket state is corrupt and -// all future writes will return an error. A zero value for t means writes will -// not time out. -func (c *Conn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = t - return nil -} - -// Read methods - -func (c *Conn) advanceFrame() (int, error) { - // 1. Skip remainder of previous frame. - - if c.readRemaining > 0 { - if _, err := io.CopyN(io.Discard, c.br, c.readRemaining); err != nil { - return noFrame, err - } - } - - // 2. Read and parse first two bytes of frame header. - // To aid debugging, collect and report all errors in the first two bytes - // of the header. - - var errors []string - - p, err := c.read(2) - if err != nil { - return noFrame, err - } - - frameType := int(p[0] & 0xf) - final := p[0]&finalBit != 0 - rsv1 := p[0]&rsv1Bit != 0 - rsv2 := p[0]&rsv2Bit != 0 - rsv3 := p[0]&rsv3Bit != 0 - mask := p[1]&maskBit != 0 - _ = c.setReadRemaining(int64(p[1] & 0x7f)) // will not fail because argument is >= 0 - - c.readDecompress = false - if rsv1 { - if c.newDecompressionReader != nil { - c.readDecompress = true - } else { - errors = append(errors, "RSV1 set") - } - } - - if rsv2 { - errors = append(errors, "RSV2 set") - } - - if rsv3 { - errors = append(errors, "RSV3 set") - } - - switch frameType { - case CloseMessage, PingMessage, PongMessage: - if c.readRemaining > maxControlFramePayloadSize { - errors = append(errors, "len > 125 for control") - } - if !final { - errors = append(errors, "FIN not set on control") - } - case TextMessage, BinaryMessage: - if !c.readFinal { - errors = append(errors, "data before FIN") - } - c.readFinal = final - case continuationFrame: - if c.readFinal { - errors = append(errors, "continuation after FIN") - } - c.readFinal = final - default: - errors = append(errors, "bad opcode "+strconv.Itoa(frameType)) - } - - if mask != c.isServer { - errors = append(errors, "bad MASK") - } - - if len(errors) > 0 { - return noFrame, c.handleProtocolError(strings.Join(errors, ", ")) - } - - // 3. Read and parse frame length as per - // https://tools.ietf.org/html/rfc6455#section-5.2 - // - // The length of the "Payload data", in bytes: if 0-125, that is the payload - // length. - // - If 126, the following 2 bytes interpreted as a 16-bit unsigned - // integer are the payload length. - // - If 127, the following 8 bytes interpreted as - // a 64-bit unsigned integer (the most significant bit MUST be 0) are the - // payload length. Multibyte length quantities are expressed in network byte - // order. - - switch c.readRemaining { - case 126: - p, err := c.read(2) - if err != nil { - return noFrame, err - } - - if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil { - return noFrame, err - } - case 127: - p, err := c.read(8) - if err != nil { - return noFrame, err - } - - if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil { - return noFrame, err - } - } - - // 4. Handle frame masking. - - if mask { - c.readMaskPos = 0 - p, err := c.read(len(c.readMaskKey)) - if err != nil { - return noFrame, err - } - copy(c.readMaskKey[:], p) - } - - // 5. For text and binary messages, enforce read limit and return. - - if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { - - c.readLength += c.readRemaining - // Don't allow readLength to overflow in the presence of a large readRemaining - // counter. - if c.readLength < 0 { - return noFrame, ErrReadLimit - } - - if c.readLimit > 0 && c.readLength > c.readLimit { - // Make a best effort to send a close message describing the problem. - _ = c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) - return noFrame, ErrReadLimit - } - - return frameType, nil - } - - // 6. Read control frame payload. - - var payload []byte - if c.readRemaining > 0 { - payload, err = c.read(int(c.readRemaining)) - _ = c.setReadRemaining(0) // will not fail because argument is >= 0 - if err != nil { - return noFrame, err - } - if c.isServer { - maskBytes(c.readMaskKey, 0, payload) - } - } - - // 7. Process control frame payload. - - switch frameType { - case PongMessage: - if err := c.handlePong(string(payload)); err != nil { - return noFrame, err - } - case PingMessage: - if err := c.handlePing(string(payload)); err != nil { - return noFrame, err - } - case CloseMessage: - closeCode := CloseNoStatusReceived - closeText := "" - if len(payload) >= 2 { - closeCode = int(binary.BigEndian.Uint16(payload)) - if !isValidReceivedCloseCode(closeCode) { - return noFrame, c.handleProtocolError("bad close code " + strconv.Itoa(closeCode)) - } - closeText = string(payload[2:]) - if !utf8.ValidString(closeText) { - return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") - } - } - if err := c.handleClose(closeCode, closeText); err != nil { - return noFrame, err - } - return noFrame, &CloseError{Code: closeCode, Text: closeText} - } - - return frameType, nil -} - -func (c *Conn) handleProtocolError(message string) error { - data := FormatCloseMessage(CloseProtocolError, message) - if len(data) > maxControlFramePayloadSize { - data = data[:maxControlFramePayloadSize] - } - // Make a best effor to send a close message describing the problem. - _ = c.WriteControl(CloseMessage, data, time.Now().Add(writeWait)) - return errors.New("websocket: " + message) -} - -// NextReader returns the next data message received from the peer. The -// returned messageType is either TextMessage or BinaryMessage. -// -// There can be at most one open reader on a connection. NextReader discards -// the previous message if the application has not already consumed it. -// -// Applications must break out of the application's read loop when this method -// returns a non-nil error value. Errors returned from this method are -// permanent. Once this method returns a non-nil error, all subsequent calls to -// this method return the same error. -func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { - // Close previous reader, only relevant for decompression. - if c.reader != nil { - c.reader.Close() - c.reader = nil - } - - c.messageReader = nil - c.readLength = 0 - - for c.readErr == nil { - frameType, err := c.advanceFrame() - if err != nil { - c.readErr = err - break - } - - if frameType == TextMessage || frameType == BinaryMessage { - c.messageReader = &messageReader{c} - c.reader = c.messageReader - if c.readDecompress { - c.reader = c.newDecompressionReader(c.reader) - } - return frameType, c.reader, nil - } - } - - // Applications that do handle the error returned from this method spin in - // tight loop on connection failure. To help application developers detect - // this error, panic on repeated reads to the failed connection. - c.readErrCount++ - if c.readErrCount >= 1000 { - panic("repeated read on failed websocket connection") - } - - return noFrame, nil, c.readErr -} - -type messageReader struct{ c *Conn } - -func (r *messageReader) Read(b []byte) (int, error) { - c := r.c - if c.messageReader != r { - return 0, io.EOF - } - - for c.readErr == nil { - - if c.readRemaining > 0 { - if int64(len(b)) > c.readRemaining { - b = b[:c.readRemaining] - } - n, err := c.br.Read(b) - c.readErr = err - if c.isServer { - c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) - } - rem := c.readRemaining - rem -= int64(n) - _ = c.setReadRemaining(rem) // rem is guaranteed to be >= 0 - if c.readRemaining > 0 && c.readErr == io.EOF { - c.readErr = errUnexpectedEOF - } - return n, c.readErr - } - - if c.readFinal { - c.messageReader = nil - return 0, io.EOF - } - - frameType, err := c.advanceFrame() - switch { - case err != nil: - c.readErr = err - case frameType == TextMessage || frameType == BinaryMessage: - c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") - } - } - - err := c.readErr - if err == io.EOF && c.messageReader == r { - err = errUnexpectedEOF - } - return 0, err -} - -func (r *messageReader) Close() error { - return nil -} - -// ReadMessage is a helper method for getting a reader using NextReader and -// reading from that reader to a buffer. -func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { - var r io.Reader - messageType, r, err = c.NextReader() - if err != nil { - return messageType, nil, err - } - p, err = io.ReadAll(r) - return messageType, p, err -} - -// SetReadDeadline sets the read deadline on the underlying network connection. -// After a read has timed out, the websocket connection state is corrupt and -// all future reads will return an error. A zero value for t means reads will -// not time out. -func (c *Conn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a -// message exceeds the limit, the connection sends a close message to the peer -// and returns ErrReadLimit to the application. -func (c *Conn) SetReadLimit(limit int64) { - c.readLimit = limit -} - -// CloseHandler returns the current close handler -func (c *Conn) CloseHandler() func(code int, text string) error { - return c.handleClose -} - -// SetCloseHandler sets the handler for close messages received from the peer. -// The code argument to h is the received close code or CloseNoStatusReceived -// if the close message is empty. The default close handler sends a close -// message back to the peer. -// -// The handler function is called from the NextReader, ReadMessage and message -// reader Read methods. The application must read the connection to process -// close messages as described in the section on Control Messages above. -// -// The connection read methods return a CloseError when a close message is -// received. Most applications should handle close messages as part of their -// normal error handling. Applications should only set a close handler when the -// application must perform some action before sending a close message back to -// the peer. -func (c *Conn) SetCloseHandler(h func(code int, text string) error) { - if h == nil { - h = func(code int, text string) error { - message := FormatCloseMessage(code, "") - // Make a best effor to send the close message. - _ = c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) - return nil - } - } - c.handleClose = h -} - -// PingHandler returns the current ping handler -func (c *Conn) PingHandler() func(appData string) error { - return c.handlePing -} - -// SetPingHandler sets the handler for ping messages received from the peer. -// The appData argument to h is the PING message application data. The default -// ping handler sends a pong to the peer. -// -// The handler function is called from the NextReader, ReadMessage and message -// reader Read methods. The application must read the connection to process -// ping messages as described in the section on Control Messages above. -func (c *Conn) SetPingHandler(h func(appData string) error) { - if h == nil { - h = func(message string) error { - // Make a best effort to send the pong message. - _ = c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) - return nil - } - } - c.handlePing = h -} - -// PongHandler returns the current pong handler -func (c *Conn) PongHandler() func(appData string) error { - return c.handlePong -} - -// SetPongHandler sets the handler for pong messages received from the peer. -// The appData argument to h is the PONG message application data. The default -// pong handler does nothing. -// -// The handler function is called from the NextReader, ReadMessage and message -// reader Read methods. The application must read the connection to process -// pong messages as described in the section on Control Messages above. -func (c *Conn) SetPongHandler(h func(appData string) error) { - if h == nil { - h = func(string) error { return nil } - } - c.handlePong = h -} - -// NetConn returns the underlying connection that is wrapped by c. -// Note that writing to or reading from this connection directly will corrupt the -// WebSocket connection. -func (c *Conn) NetConn() net.Conn { - return c.conn -} - -// UnderlyingConn returns the internal net.Conn. This can be used to further -// modifications to connection specific flags. -// Deprecated: Use the NetConn method. -func (c *Conn) UnderlyingConn() net.Conn { - return c.conn -} - -// EnableWriteCompression enables and disables write compression of -// subsequent text and binary messages. This function is a noop if -// compression was not negotiated with the peer. -func (c *Conn) EnableWriteCompression(enable bool) { - c.enableWriteCompression = enable -} - -// SetCompressionLevel sets the flate compression level for subsequent text and -// binary messages. This function is a noop if compression was not negotiated -// with the peer. See the compress/flate package for a description of -// compression levels. -func (c *Conn) SetCompressionLevel(level int) error { - if !isValidCompressionLevel(level) { - return errors.New("websocket: invalid compression level") - } - c.compressionLevel = level - return nil -} - -// FormatCloseMessage formats closeCode and text as a WebSocket close message. -// An empty message is returned for code CloseNoStatusReceived. -func FormatCloseMessage(closeCode int, text string) []byte { - if closeCode == CloseNoStatusReceived { - // Return empty message because it's illegal to send - // CloseNoStatusReceived. Return non-nil value in case application - // checks for nil. - return []byte{} - } - buf := make([]byte, 2+len(text)) - binary.BigEndian.PutUint16(buf, uint16(closeCode)) - copy(buf[2:], text) - return buf -} diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go deleted file mode 100644 index 8db0cef95..000000000 --- a/vendor/github.com/gorilla/websocket/doc.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package websocket implements the WebSocket protocol defined in RFC 6455. -// -// Overview -// -// The Conn type represents a WebSocket connection. A server application calls -// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: -// -// var upgrader = websocket.Upgrader{ -// ReadBufferSize: 1024, -// WriteBufferSize: 1024, -// } -// -// func handler(w http.ResponseWriter, r *http.Request) { -// conn, err := upgrader.Upgrade(w, r, nil) -// if err != nil { -// log.Println(err) -// return -// } -// ... Use conn to send and receive messages. -// } -// -// Call the connection's WriteMessage and ReadMessage methods to send and -// receive messages as a slice of bytes. This snippet of code shows how to echo -// messages using these methods: -// -// for { -// messageType, p, err := conn.ReadMessage() -// if err != nil { -// log.Println(err) -// return -// } -// if err := conn.WriteMessage(messageType, p); err != nil { -// log.Println(err) -// return -// } -// } -// -// In above snippet of code, p is a []byte and messageType is an int with value -// websocket.BinaryMessage or websocket.TextMessage. -// -// An application can also send and receive messages using the io.WriteCloser -// and io.Reader interfaces. To send a message, call the connection NextWriter -// method to get an io.WriteCloser, write the message to the writer and close -// the writer when done. To receive a message, call the connection NextReader -// method to get an io.Reader and read until io.EOF is returned. This snippet -// shows how to echo messages using the NextWriter and NextReader methods: -// -// for { -// messageType, r, err := conn.NextReader() -// if err != nil { -// return -// } -// w, err := conn.NextWriter(messageType) -// if err != nil { -// return err -// } -// if _, err := io.Copy(w, r); err != nil { -// return err -// } -// if err := w.Close(); err != nil { -// return err -// } -// } -// -// Data Messages -// -// The WebSocket protocol distinguishes between text and binary data messages. -// Text messages are interpreted as UTF-8 encoded text. The interpretation of -// binary messages is left to the application. -// -// This package uses the TextMessage and BinaryMessage integer constants to -// identify the two data message types. The ReadMessage and NextReader methods -// return the type of the received message. The messageType argument to the -// WriteMessage and NextWriter methods specifies the type of a sent message. -// -// It is the application's responsibility to ensure that text messages are -// valid UTF-8 encoded text. -// -// Control Messages -// -// The WebSocket protocol defines three types of control messages: close, ping -// and pong. Call the connection WriteControl, WriteMessage or NextWriter -// methods to send a control message to the peer. -// -// Connections handle received close messages by calling the handler function -// set with the SetCloseHandler method and by returning a *CloseError from the -// NextReader, ReadMessage or the message Read method. The default close -// handler sends a close message to the peer. -// -// Connections handle received ping messages by calling the handler function -// set with the SetPingHandler method. The default ping handler sends a pong -// message to the peer. -// -// Connections handle received pong messages by calling the handler function -// set with the SetPongHandler method. The default pong handler does nothing. -// If an application sends ping messages, then the application should set a -// pong handler to receive the corresponding pong. -// -// The control message handler functions are called from the NextReader, -// ReadMessage and message reader Read methods. The default close and ping -// handlers can block these methods for a short time when the handler writes to -// the connection. -// -// The application must read the connection to process close, ping and pong -// messages sent from the peer. If the application is not otherwise interested -// in messages from the peer, then the application should start a goroutine to -// read and discard messages from the peer. A simple example is: -// -// func readLoop(c *websocket.Conn) { -// for { -// if _, _, err := c.NextReader(); err != nil { -// c.Close() -// break -// } -// } -// } -// -// Concurrency -// -// Connections support one concurrent reader and one concurrent writer. -// -// Applications are responsible for ensuring that no more than one goroutine -// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, -// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and -// that no more than one goroutine calls the read methods (NextReader, -// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) -// concurrently. -// -// The Close and WriteControl methods can be called concurrently with all other -// methods. -// -// Origin Considerations -// -// Web browsers allow Javascript applications to open a WebSocket connection to -// any host. It's up to the server to enforce an origin policy using the Origin -// request header sent by the browser. -// -// The Upgrader calls the function specified in the CheckOrigin field to check -// the origin. If the CheckOrigin function returns false, then the Upgrade -// method fails the WebSocket handshake with HTTP status 403. -// -// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail -// the handshake if the Origin request header is present and the Origin host is -// not equal to the Host request header. -// -// The deprecated package-level Upgrade function does not perform origin -// checking. The application is responsible for checking the Origin header -// before calling the Upgrade function. -// -// Buffers -// -// Connections buffer network input and output to reduce the number -// of system calls when reading or writing messages. -// -// Write buffers are also used for constructing WebSocket frames. See RFC 6455, -// Section 5 for a discussion of message framing. A WebSocket frame header is -// written to the network each time a write buffer is flushed to the network. -// Decreasing the size of the write buffer can increase the amount of framing -// overhead on the connection. -// -// The buffer sizes in bytes are specified by the ReadBufferSize and -// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default -// size of 4096 when a buffer size field is set to zero. The Upgrader reuses -// buffers created by the HTTP server when a buffer size field is set to zero. -// The HTTP server buffers have a size of 4096 at the time of this writing. -// -// The buffer sizes do not limit the size of a message that can be read or -// written by a connection. -// -// Buffers are held for the lifetime of the connection by default. If the -// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the -// write buffer only when writing a message. -// -// Applications should tune the buffer sizes to balance memory use and -// performance. Increasing the buffer size uses more memory, but can reduce the -// number of system calls to read or write the network. In the case of writing, -// increasing the buffer size can reduce the number of frame headers written to -// the network. -// -// Some guidelines for setting buffer parameters are: -// -// Limit the buffer sizes to the maximum expected message size. Buffers larger -// than the largest message do not provide any benefit. -// -// Depending on the distribution of message sizes, setting the buffer size to -// a value less than the maximum expected message size can greatly reduce memory -// use with a small impact on performance. Here's an example: If 99% of the -// messages are smaller than 256 bytes and the maximum message size is 512 -// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls -// than a buffer size of 512 bytes. The memory savings is 50%. -// -// A write buffer pool is useful when the application has a modest number -// writes over a large number of connections. when buffers are pooled, a larger -// buffer size has a reduced impact on total memory use and has the benefit of -// reducing system calls and frame overhead. -// -// Compression EXPERIMENTAL -// -// Per message compression extensions (RFC 7692) are experimentally supported -// by this package in a limited capacity. Setting the EnableCompression option -// to true in Dialer or Upgrader will attempt to negotiate per message deflate -// support. -// -// var upgrader = websocket.Upgrader{ -// EnableCompression: true, -// } -// -// If compression was successfully negotiated with the connection's peer, any -// message received in compressed form will be automatically decompressed. -// All Read methods will return uncompressed bytes. -// -// Per message compression of messages written to a connection can be enabled -// or disabled by calling the corresponding Conn method: -// -// conn.EnableWriteCompression(false) -// -// Currently this package does not support compression with "context takeover". -// This means that messages must be compressed and decompressed in isolation, -// without retaining sliding window or dictionary state across messages. For -// more details refer to RFC 7692. -// -// Use of compression is experimental and may result in decreased performance. -package websocket diff --git a/vendor/github.com/gorilla/websocket/join.go b/vendor/github.com/gorilla/websocket/join.go deleted file mode 100644 index c64f8c829..000000000 --- a/vendor/github.com/gorilla/websocket/join.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "io" - "strings" -) - -// JoinMessages concatenates received messages to create a single io.Reader. -// The string term is appended to each message. The returned reader does not -// support concurrent calls to the Read method. -func JoinMessages(c *Conn, term string) io.Reader { - return &joinReader{c: c, term: term} -} - -type joinReader struct { - c *Conn - term string - r io.Reader -} - -func (r *joinReader) Read(p []byte) (int, error) { - if r.r == nil { - var err error - _, r.r, err = r.c.NextReader() - if err != nil { - return 0, err - } - if r.term != "" { - r.r = io.MultiReader(r.r, strings.NewReader(r.term)) - } - } - n, err := r.r.Read(p) - if err == io.EOF { - err = nil - r.r = nil - } - return n, err -} diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go deleted file mode 100644 index dc2c1f641..000000000 --- a/vendor/github.com/gorilla/websocket/json.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "encoding/json" - "io" -) - -// WriteJSON writes the JSON encoding of v as a message. -// -// Deprecated: Use c.WriteJSON instead. -func WriteJSON(c *Conn, v interface{}) error { - return c.WriteJSON(v) -} - -// WriteJSON writes the JSON encoding of v as a message. -// -// See the documentation for encoding/json Marshal for details about the -// conversion of Go values to JSON. -func (c *Conn) WriteJSON(v interface{}) error { - w, err := c.NextWriter(TextMessage) - if err != nil { - return err - } - err1 := json.NewEncoder(w).Encode(v) - err2 := w.Close() - if err1 != nil { - return err1 - } - return err2 -} - -// ReadJSON reads the next JSON-encoded message from the connection and stores -// it in the value pointed to by v. -// -// Deprecated: Use c.ReadJSON instead. -func ReadJSON(c *Conn, v interface{}) error { - return c.ReadJSON(v) -} - -// ReadJSON reads the next JSON-encoded message from the connection and stores -// it in the value pointed to by v. -// -// See the documentation for the encoding/json Unmarshal function for details -// about the conversion of JSON to a Go value. -func (c *Conn) ReadJSON(v interface{}) error { - _, r, err := c.NextReader() - if err != nil { - return err - } - err = json.NewDecoder(r).Decode(v) - if err == io.EOF { - // One value is expected in the message. - err = io.ErrUnexpectedEOF - } - return err -} diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go deleted file mode 100644 index d0742bf2a..000000000 --- a/vendor/github.com/gorilla/websocket/mask.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of -// this source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -//go:build !appengine -// +build !appengine - -package websocket - -import "unsafe" - -const wordSize = int(unsafe.Sizeof(uintptr(0))) - -func maskBytes(key [4]byte, pos int, b []byte) int { - // Mask one byte at a time for small buffers. - if len(b) < 2*wordSize { - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - return pos & 3 - } - - // Mask one byte at a time to word boundary. - if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { - n = wordSize - n - for i := range b[:n] { - b[i] ^= key[pos&3] - pos++ - } - b = b[n:] - } - - // Create aligned word size key. - var k [wordSize]byte - for i := range k { - k[i] = key[(pos+i)&3] - } - kw := *(*uintptr)(unsafe.Pointer(&k)) - - // Mask one word at a time. - n := (len(b) / wordSize) * wordSize - for i := 0; i < n; i += wordSize { - *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw - } - - // Mask one byte at a time for remaining bytes. - b = b[n:] - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - - return pos & 3 -} diff --git a/vendor/github.com/gorilla/websocket/mask_safe.go b/vendor/github.com/gorilla/websocket/mask_safe.go deleted file mode 100644 index 36250ca7c..000000000 --- a/vendor/github.com/gorilla/websocket/mask_safe.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of -// this source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -//go:build appengine -// +build appengine - -package websocket - -func maskBytes(key [4]byte, pos int, b []byte) int { - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - return pos & 3 -} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go deleted file mode 100644 index c854225e9..000000000 --- a/vendor/github.com/gorilla/websocket/prepared.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bytes" - "net" - "sync" - "time" -) - -// PreparedMessage caches on the wire representations of a message payload. -// Use PreparedMessage to efficiently send a message payload to multiple -// connections. PreparedMessage is especially useful when compression is used -// because the CPU and memory expensive compression operation can be executed -// once for a given set of compression options. -type PreparedMessage struct { - messageType int - data []byte - mu sync.Mutex - frames map[prepareKey]*preparedFrame -} - -// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. -type prepareKey struct { - isServer bool - compress bool - compressionLevel int -} - -// preparedFrame contains data in wire representation. -type preparedFrame struct { - once sync.Once - data []byte -} - -// NewPreparedMessage returns an initialized PreparedMessage. You can then send -// it to connection using WritePreparedMessage method. Valid wire -// representation will be calculated lazily only once for a set of current -// connection options. -func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { - pm := &PreparedMessage{ - messageType: messageType, - frames: make(map[prepareKey]*preparedFrame), - data: data, - } - - // Prepare a plain server frame. - _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) - if err != nil { - return nil, err - } - - // To protect against caller modifying the data argument, remember the data - // copied to the plain server frame. - pm.data = frameData[len(frameData)-len(data):] - return pm, nil -} - -func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { - pm.mu.Lock() - frame, ok := pm.frames[key] - if !ok { - frame = &preparedFrame{} - pm.frames[key] = frame - } - pm.mu.Unlock() - - var err error - frame.once.Do(func() { - // Prepare a frame using a 'fake' connection. - // TODO: Refactor code in conn.go to allow more direct construction of - // the frame. - mu := make(chan struct{}, 1) - mu <- struct{}{} - var nc prepareConn - c := &Conn{ - conn: &nc, - mu: mu, - isServer: key.isServer, - compressionLevel: key.compressionLevel, - enableWriteCompression: true, - writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), - } - if key.compress { - c.newCompressionWriter = compressNoContextTakeover - } - err = c.WriteMessage(pm.messageType, pm.data) - frame.data = nc.buf.Bytes() - }) - return pm.messageType, frame.data, err -} - -type prepareConn struct { - buf bytes.Buffer - net.Conn -} - -func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } -func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/vendor/github.com/gorilla/websocket/proxy.go b/vendor/github.com/gorilla/websocket/proxy.go deleted file mode 100644 index d716a0588..000000000 --- a/vendor/github.com/gorilla/websocket/proxy.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "context" - "encoding/base64" - "errors" - "net" - "net/http" - "net/url" - "strings" - - "golang.org/x/net/proxy" -) - -type netDialerFunc func(ctx context.Context, network, addr string) (net.Conn, error) - -func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { - return fn(context.Background(), network, addr) -} - -func (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { - return fn(ctx, network, addr) -} - -func proxyFromURL(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) { - if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" { - return (&httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDial}).DialContext, nil - } - dialer, err := proxy.FromURL(proxyURL, forwardDial) - if err != nil { - return nil, err - } - if d, ok := dialer.(proxy.ContextDialer); ok { - return d.DialContext, nil - } - return func(ctx context.Context, net, addr string) (net.Conn, error) { - return dialer.Dial(net, addr) - }, nil -} - -type httpProxyDialer struct { - proxyURL *url.URL - forwardDial netDialerFunc -} - -func (hpd *httpProxyDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) { - hostPort, _ := hostPortNoPort(hpd.proxyURL) - conn, err := hpd.forwardDial(ctx, network, hostPort) - if err != nil { - return nil, err - } - - connectHeader := make(http.Header) - if user := hpd.proxyURL.User; user != nil { - proxyUser := user.Username() - if proxyPassword, passwordSet := user.Password(); passwordSet { - credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) - connectHeader.Set("Proxy-Authorization", "Basic "+credential) - } - } - connectReq := &http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Opaque: addr}, - Host: addr, - Header: connectHeader, - } - - if err := connectReq.Write(conn); err != nil { - conn.Close() - return nil, err - } - - // Read response. It's OK to use and discard buffered reader here because - // the remote server does not speak until spoken to. - br := bufio.NewReader(conn) - resp, err := http.ReadResponse(br, connectReq) - if err != nil { - conn.Close() - return nil, err - } - - // Close the response body to silence false positives from linters. Reset - // the buffered reader first to ensure that Close() does not read from - // conn. - // Note: Applications must call resp.Body.Close() on a response returned - // http.ReadResponse to inspect trailers or read another response from the - // buffered reader. The call to resp.Body.Close() does not release - // resources. - br.Reset(bytes.NewReader(nil)) - _ = resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - _ = conn.Close() - f := strings.SplitN(resp.Status, " ", 2) - return nil, errors.New(f[1]) - } - return conn, nil -} diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go deleted file mode 100644 index 02ea01fdc..000000000 --- a/vendor/github.com/gorilla/websocket/server.go +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "net" - "net/http" - "net/url" - "strings" - "time" -) - -// HandshakeError describes an error with the handshake from the peer. -type HandshakeError struct { - message string -} - -func (e HandshakeError) Error() string { return e.message } - -// Upgrader specifies parameters for upgrading an HTTP connection to a -// WebSocket connection. -// -// It is safe to call Upgrader's methods concurrently. -type Upgrader struct { - // HandshakeTimeout specifies the duration for the handshake to complete. - HandshakeTimeout time.Duration - - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer - // size is zero, then buffers allocated by the HTTP server are used. The - // I/O buffer sizes do not limit the size of the messages that can be sent - // or received. - ReadBufferSize, WriteBufferSize int - - // WriteBufferPool is a pool of buffers for write operations. If the value - // is not set, then write buffers are allocated to the connection for the - // lifetime of the connection. - // - // A pool is most useful when the application has a modest volume of writes - // across a large number of connections. - // - // Applications should use a single pool for each unique value of - // WriteBufferSize. - WriteBufferPool BufferPool - - // Subprotocols specifies the server's supported protocols in order of - // preference. If this field is not nil, then the Upgrade method negotiates a - // subprotocol by selecting the first match in this list with a protocol - // requested by the client. If there's no match, then no protocol is - // negotiated (the Sec-Websocket-Protocol header is not included in the - // handshake response). - Subprotocols []string - - // Error specifies the function for generating HTTP error responses. If Error - // is nil, then http.Error is used to generate the HTTP response. - Error func(w http.ResponseWriter, r *http.Request, status int, reason error) - - // CheckOrigin returns true if the request Origin header is acceptable. If - // CheckOrigin is nil, then a safe default is used: return false if the - // Origin request header is present and the origin host is not equal to - // request Host header. - // - // A CheckOrigin function should carefully validate the request origin to - // prevent cross-site request forgery. - CheckOrigin func(r *http.Request) bool - - // EnableCompression specify if the server should attempt to negotiate per - // message compression (RFC 7692). Setting this value to true does not - // guarantee that compression will be supported. Currently only "no context - // takeover" modes are supported. - EnableCompression bool -} - -func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { - err := HandshakeError{reason} - if u.Error != nil { - u.Error(w, r, status, err) - } else { - w.Header().Set("Sec-Websocket-Version", "13") - http.Error(w, http.StatusText(status), status) - } - return nil, err -} - -// checkSameOrigin returns true if the origin is not set or is equal to the request host. -func checkSameOrigin(r *http.Request) bool { - origin := r.Header["Origin"] - if len(origin) == 0 { - return true - } - u, err := url.Parse(origin[0]) - if err != nil { - return false - } - return equalASCIIFold(u.Host, r.Host) -} - -func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { - if u.Subprotocols != nil { - clientProtocols := Subprotocols(r) - for _, clientProtocol := range clientProtocols { - for _, serverProtocol := range u.Subprotocols { - if clientProtocol == serverProtocol { - return clientProtocol - } - } - } - } else if responseHeader != nil { - return responseHeader.Get("Sec-Websocket-Protocol") - } - return "" -} - -// Upgrade upgrades the HTTP server connection to the WebSocket protocol. -// -// The responseHeader is included in the response to the client's upgrade -// request. Use the responseHeader to specify cookies (Set-Cookie). To specify -// subprotocols supported by the server, set Upgrader.Subprotocols directly. -// -// If the upgrade fails, then Upgrade replies to the client with an HTTP error -// response. -func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { - const badHandshake = "websocket: the client is not using the websocket protocol: " - - if !tokenListContainsValue(r.Header, "Connection", "upgrade") { - return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header") - } - - if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { - w.Header().Set("Upgrade", "websocket") - return u.returnError(w, r, http.StatusUpgradeRequired, badHandshake+"'websocket' token not found in 'Upgrade' header") - } - - if r.Method != http.MethodGet { - return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET") - } - - if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { - return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") - } - - if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { - return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported") - } - - checkOrigin := u.CheckOrigin - if checkOrigin == nil { - checkOrigin = checkSameOrigin - } - if !checkOrigin(r) { - return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin") - } - - challengeKey := r.Header.Get("Sec-Websocket-Key") - if !isValidChallengeKey(challengeKey) { - return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length") - } - - subprotocol := u.selectSubprotocol(r, responseHeader) - - // Negotiate PMCE - var compress bool - if u.EnableCompression { - for _, ext := range parseExtensions(r.Header) { - if ext[""] != "permessage-deflate" { - continue - } - compress = true - break - } - } - - netConn, brw, err := http.NewResponseController(w).Hijack() - if err != nil { - return u.returnError(w, r, http.StatusInternalServerError, - "websocket: hijack: "+err.Error()) - } - - // Close the network connection when returning an error. The variable - // netConn is set to nil before the success return at the end of the - // function. - defer func() { - if netConn != nil { - // It's safe to ignore the error from Close() because this code is - // only executed when returning a more important error to the - // application. - _ = netConn.Close() - } - }() - - var br *bufio.Reader - if u.ReadBufferSize == 0 && brw.Reader.Size() > 256 { - // Use hijacked buffered reader as the connection reader. - br = brw.Reader - } else if brw.Reader.Buffered() > 0 { - // Wrap the network connection to read buffered data in brw.Reader - // before reading from the network connection. This should be rare - // because a client must not send message data before receiving the - // handshake response. - netConn = &brNetConn{br: brw.Reader, Conn: netConn} - } - - buf := brw.Writer.AvailableBuffer() - - var writeBuf []byte - if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 { - // Reuse hijacked write buffer as connection buffer. - writeBuf = buf - } - - c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf) - c.subprotocol = subprotocol - - if compress { - c.newCompressionWriter = compressNoContextTakeover - c.newDecompressionReader = decompressNoContextTakeover - } - - // Use larger of hijacked buffer and connection write buffer for header. - p := buf - if len(c.writeBuf) > len(p) { - p = c.writeBuf - } - p = p[:0] - - p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) - p = append(p, computeAcceptKey(challengeKey)...) - p = append(p, "\r\n"...) - if c.subprotocol != "" { - p = append(p, "Sec-WebSocket-Protocol: "...) - p = append(p, c.subprotocol...) - p = append(p, "\r\n"...) - } - if compress { - p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) - } - for k, vs := range responseHeader { - if k == "Sec-Websocket-Protocol" { - continue - } - for _, v := range vs { - p = append(p, k...) - p = append(p, ": "...) - for i := 0; i < len(v); i++ { - b := v[i] - if b <= 31 { - // prevent response splitting. - b = ' ' - } - p = append(p, b) - } - p = append(p, "\r\n"...) - } - } - p = append(p, "\r\n"...) - - if u.HandshakeTimeout > 0 { - if err := netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)); err != nil { - return nil, err - } - } else { - // Clear deadlines set by HTTP server. - if err := netConn.SetDeadline(time.Time{}); err != nil { - return nil, err - } - } - - if _, err = netConn.Write(p); err != nil { - return nil, err - } - if u.HandshakeTimeout > 0 { - if err := netConn.SetWriteDeadline(time.Time{}); err != nil { - return nil, err - } - } - - // Success! Set netConn to nil to stop the deferred function above from - // closing the network connection. - netConn = nil - - return c, nil -} - -// Upgrade upgrades the HTTP server connection to the WebSocket protocol. -// -// Deprecated: Use websocket.Upgrader instead. -// -// Upgrade does not perform origin checking. The application is responsible for -// checking the Origin header before calling Upgrade. An example implementation -// of the same origin policy check is: -// -// if req.Header.Get("Origin") != "http://"+req.Host { -// http.Error(w, "Origin not allowed", http.StatusForbidden) -// return -// } -// -// If the endpoint supports subprotocols, then the application is responsible -// for negotiating the protocol used on the connection. Use the Subprotocols() -// function to get the subprotocols requested by the client. Use the -// Sec-Websocket-Protocol response header to specify the subprotocol selected -// by the application. -// -// The responseHeader is included in the response to the client's upgrade -// request. Use the responseHeader to specify cookies (Set-Cookie) and the -// negotiated subprotocol (Sec-Websocket-Protocol). -// -// The connection buffers IO to the underlying network connection. The -// readBufSize and writeBufSize parameters specify the size of the buffers to -// use. Messages can be larger than the buffers. -// -// If the request is not a valid WebSocket handshake, then Upgrade returns an -// error of type HandshakeError. Applications should handle this error by -// replying to the client with an HTTP error response. -func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { - u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} - u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { - // don't return errors to maintain backwards compatibility - } - u.CheckOrigin = func(r *http.Request) bool { - // allow all connections by default - return true - } - return u.Upgrade(w, r, responseHeader) -} - -// Subprotocols returns the subprotocols requested by the client in the -// Sec-Websocket-Protocol header. -func Subprotocols(r *http.Request) []string { - h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) - if h == "" { - return nil - } - protocols := strings.Split(h, ",") - for i := range protocols { - protocols[i] = strings.TrimSpace(protocols[i]) - } - return protocols -} - -// IsWebSocketUpgrade returns true if the client requested upgrade to the -// WebSocket protocol. -func IsWebSocketUpgrade(r *http.Request) bool { - return tokenListContainsValue(r.Header, "Connection", "upgrade") && - tokenListContainsValue(r.Header, "Upgrade", "websocket") -} - -type brNetConn struct { - br *bufio.Reader - net.Conn -} - -func (b *brNetConn) Read(p []byte) (n int, err error) { - if b.br != nil { - // Limit read to buferred data. - if n := b.br.Buffered(); len(p) > n { - p = p[:n] - } - n, err = b.br.Read(p) - if b.br.Buffered() == 0 { - b.br = nil - } - return n, err - } - return b.Conn.Read(p) -} - -// NetConn returns the underlying connection that is wrapped by b. -func (b *brNetConn) NetConn() net.Conn { - return b.Conn -} - diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go deleted file mode 100644 index 31a5dee64..000000000 --- a/vendor/github.com/gorilla/websocket/util.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "io" - "net/http" - "strings" - "unicode/utf8" -) - -var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") - -func computeAcceptKey(challengeKey string) string { - h := sha1.New() - h.Write([]byte(challengeKey)) - h.Write(keyGUID) - return base64.StdEncoding.EncodeToString(h.Sum(nil)) -} - -func generateChallengeKey() (string, error) { - p := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, p); err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(p), nil -} - -// Token octets per RFC 2616. -var isTokenOctet = [256]bool{ - '!': true, - '#': true, - '$': true, - '%': true, - '&': true, - '\'': true, - '*': true, - '+': true, - '-': true, - '.': true, - '0': true, - '1': true, - '2': true, - '3': true, - '4': true, - '5': true, - '6': true, - '7': true, - '8': true, - '9': true, - 'A': true, - 'B': true, - 'C': true, - 'D': true, - 'E': true, - 'F': true, - 'G': true, - 'H': true, - 'I': true, - 'J': true, - 'K': true, - 'L': true, - 'M': true, - 'N': true, - 'O': true, - 'P': true, - 'Q': true, - 'R': true, - 'S': true, - 'T': true, - 'U': true, - 'W': true, - 'V': true, - 'X': true, - 'Y': true, - 'Z': true, - '^': true, - '_': true, - '`': true, - 'a': true, - 'b': true, - 'c': true, - 'd': true, - 'e': true, - 'f': true, - 'g': true, - 'h': true, - 'i': true, - 'j': true, - 'k': true, - 'l': true, - 'm': true, - 'n': true, - 'o': true, - 'p': true, - 'q': true, - 'r': true, - 's': true, - 't': true, - 'u': true, - 'v': true, - 'w': true, - 'x': true, - 'y': true, - 'z': true, - '|': true, - '~': true, -} - -// skipSpace returns a slice of the string s with all leading RFC 2616 linear -// whitespace removed. -func skipSpace(s string) (rest string) { - i := 0 - for ; i < len(s); i++ { - if b := s[i]; b != ' ' && b != '\t' { - break - } - } - return s[i:] -} - -// nextToken returns the leading RFC 2616 token of s and the string following -// the token. -func nextToken(s string) (token, rest string) { - i := 0 - for ; i < len(s); i++ { - if !isTokenOctet[s[i]] { - break - } - } - return s[:i], s[i:] -} - -// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 -// and the string following the token or quoted string. -func nextTokenOrQuoted(s string) (value string, rest string) { - if !strings.HasPrefix(s, "\"") { - return nextToken(s) - } - s = s[1:] - for i := 0; i < len(s); i++ { - switch s[i] { - case '"': - return s[:i], s[i+1:] - case '\\': - p := make([]byte, len(s)-1) - j := copy(p, s[:i]) - escape := true - for i = i + 1; i < len(s); i++ { - b := s[i] - switch { - case escape: - escape = false - p[j] = b - j++ - case b == '\\': - escape = true - case b == '"': - return string(p[:j]), s[i+1:] - default: - p[j] = b - j++ - } - } - return "", "" - } - } - return "", "" -} - -// equalASCIIFold returns true if s is equal to t with ASCII case folding as -// defined in RFC 4790. -func equalASCIIFold(s, t string) bool { - for s != "" && t != "" { - sr, size := utf8.DecodeRuneInString(s) - s = s[size:] - tr, size := utf8.DecodeRuneInString(t) - t = t[size:] - if sr == tr { - continue - } - if 'A' <= sr && sr <= 'Z' { - sr = sr + 'a' - 'A' - } - if 'A' <= tr && tr <= 'Z' { - tr = tr + 'a' - 'A' - } - if sr != tr { - return false - } - } - return s == t -} - -// tokenListContainsValue returns true if the 1#token header with the given -// name contains a token equal to value with ASCII case folding. -func tokenListContainsValue(header http.Header, name string, value string) bool { -headers: - for _, s := range header[name] { - for { - var t string - t, s = nextToken(skipSpace(s)) - if t == "" { - continue headers - } - s = skipSpace(s) - if s != "" && s[0] != ',' { - continue headers - } - if equalASCIIFold(t, value) { - return true - } - if s == "" { - continue headers - } - s = s[1:] - } - } - return false -} - -// parseExtensions parses WebSocket extensions from a header. -func parseExtensions(header http.Header) []map[string]string { - // From RFC 6455: - // - // Sec-WebSocket-Extensions = extension-list - // extension-list = 1#extension - // extension = extension-token *( ";" extension-param ) - // extension-token = registered-token - // registered-token = token - // extension-param = token [ "=" (token | quoted-string) ] - // ;When using the quoted-string syntax variant, the value - // ;after quoted-string unescaping MUST conform to the - // ;'token' ABNF. - - var result []map[string]string -headers: - for _, s := range header["Sec-Websocket-Extensions"] { - for { - var t string - t, s = nextToken(skipSpace(s)) - if t == "" { - continue headers - } - ext := map[string]string{"": t} - for { - s = skipSpace(s) - if !strings.HasPrefix(s, ";") { - break - } - var k string - k, s = nextToken(skipSpace(s[1:])) - if k == "" { - continue headers - } - s = skipSpace(s) - var v string - if strings.HasPrefix(s, "=") { - v, s = nextTokenOrQuoted(skipSpace(s[1:])) - s = skipSpace(s) - } - if s != "" && s[0] != ',' && s[0] != ';' { - continue headers - } - ext[k] = v - } - if s != "" && s[0] != ',' { - continue headers - } - result = append(result, ext) - if s == "" { - continue headers - } - s = s[1:] - } - } - return result -} - -// isValidChallengeKey checks if the argument meets RFC6455 specification. -func isValidChallengeKey(s string) bool { - // From RFC6455: - // - // A |Sec-WebSocket-Key| header field with a base64-encoded (see - // Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in - // length. - - if s == "" { - return false - } - decoded, err := base64.StdEncoding.DecodeString(s) - return err == nil && len(decoded) == 16 -} diff --git a/vendor/github.com/moby/spdystream/CONTRIBUTING.md b/vendor/github.com/moby/spdystream/CONTRIBUTING.md deleted file mode 100644 index d4eddcc53..000000000 --- a/vendor/github.com/moby/spdystream/CONTRIBUTING.md +++ /dev/null @@ -1,13 +0,0 @@ -# Contributing to SpdyStream - -Want to hack on spdystream? Awesome! Here are instructions to get you -started. - -SpdyStream is a part of the [Docker](https://docker.io) project, and follows -the same rules and principles. If you're already familiar with the way -Docker does things, you'll feel right at home. - -Otherwise, go read -[Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md). - -Happy hacking! diff --git a/vendor/github.com/moby/spdystream/LICENSE b/vendor/github.com/moby/spdystream/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/vendor/github.com/moby/spdystream/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - 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/vendor/github.com/moby/spdystream/MAINTAINERS b/vendor/github.com/moby/spdystream/MAINTAINERS deleted file mode 100644 index 26e5ec828..000000000 --- a/vendor/github.com/moby/spdystream/MAINTAINERS +++ /dev/null @@ -1,40 +0,0 @@ -# Spdystream maintainers file -# -# This file describes who runs the moby/spdystream project and how. -# This is a living document - if you see something out of date or missing, speak up! -# -# It is structured to be consumable by both humans and programs. -# To extract its contents programmatically, use any TOML-compliant parser. -# -# This file is compiled into the MAINTAINERS file in docker/opensource. -# -[Org] - [Org."Core maintainers"] - people = [ - "adisky", - "dims", - "dmcgowan", - ] - -[people] - -# A reference list of all people associated with the project. -# All other sections should refer to people by their canonical key -# in the people section. - - # ADD YOURSELF HERE IN ALPHABETICAL ORDER - - [people.adisky] - Name = "Aditi Sharma" - Email = "adi.sky17@gmail.com" - GitHub = "adisky" - - [people.dims] - Name = "Davanum Srinivas" - Email = "davanum@gmail.com" - GitHub = "dims" - - [people.dmcgowan] - Name = "Derek McGowan" - Email = "derek@mcg.dev" - GitHub = "dmcgowan" diff --git a/vendor/github.com/moby/spdystream/NOTICE b/vendor/github.com/moby/spdystream/NOTICE deleted file mode 100644 index b9b11c9ab..000000000 --- a/vendor/github.com/moby/spdystream/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -SpdyStream -Copyright 2014-2021 Docker Inc. - -This product includes software developed at -Docker Inc. (https://www.docker.com/). diff --git a/vendor/github.com/moby/spdystream/README.md b/vendor/github.com/moby/spdystream/README.md deleted file mode 100644 index b84e98343..000000000 --- a/vendor/github.com/moby/spdystream/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# SpdyStream - -A multiplexed stream library using spdy - -## Usage - -Client example (connecting to mirroring server without auth) - -```go -package main - -import ( - "fmt" - "github.com/moby/spdystream" - "net" - "net/http" -) - -func main() { - conn, err := net.Dial("tcp", "localhost:8080") - if err != nil { - panic(err) - } - spdyConn, err := spdystream.NewConnection(conn, false) - if err != nil { - panic(err) - } - go spdyConn.Serve(spdystream.NoOpStreamHandler) - stream, err := spdyConn.CreateStream(http.Header{}, nil, false) - if err != nil { - panic(err) - } - - stream.Wait() - - fmt.Fprint(stream, "Writing to stream") - - buf := make([]byte, 25) - stream.Read(buf) - fmt.Println(string(buf)) - - stream.Close() -} -``` - -Server example (mirroring server without auth) - -```go -package main - -import ( - "github.com/moby/spdystream" - "net" -) - -func main() { - listener, err := net.Listen("tcp", "localhost:8080") - if err != nil { - panic(err) - } - for { - conn, err := listener.Accept() - if err != nil { - panic(err) - } - spdyConn, err := spdystream.NewConnection(conn, true) - if err != nil { - panic(err) - } - go spdyConn.Serve(spdystream.MirrorStreamHandler) - } -} -``` - -## Copyright and license - -Copyright 2013-2021 Docker, inc. Released under the [Apache 2.0 license](LICENSE). diff --git a/vendor/github.com/moby/spdystream/connection.go b/vendor/github.com/moby/spdystream/connection.go deleted file mode 100644 index 1394d0ad4..000000000 --- a/vendor/github.com/moby/spdystream/connection.go +++ /dev/null @@ -1,991 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package spdystream - -import ( - "errors" - "fmt" - "io" - "net" - "net/http" - "sync" - "time" - - "github.com/moby/spdystream/spdy" -) - -var ( - ErrInvalidStreamId = errors.New("Invalid stream id") - ErrTimeout = errors.New("Timeout occurred") - ErrReset = errors.New("Stream reset") - ErrWriteClosedStream = errors.New("Write on closed stream") -) - -const ( - FRAME_WORKERS = 5 - QUEUE_SIZE = 50 -) - -type StreamHandler func(stream *Stream) - -type AuthHandler func(header http.Header, slot uint8, parent uint32) bool - -type idleAwareFramer struct { - f *spdy.Framer - conn *Connection - writeLock sync.Mutex - resetChan chan struct{} - setTimeoutLock sync.Mutex - setTimeoutChan chan time.Duration - timeout time.Duration -} - -func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer { - iaf := &idleAwareFramer{ - f: framer, - resetChan: make(chan struct{}, 2), - // setTimeoutChan needs to be buffered to avoid deadlocks when calling setIdleTimeout at about - // the same time the connection is being closed - setTimeoutChan: make(chan time.Duration, 1), - } - return iaf -} - -func (i *idleAwareFramer) monitor() { - var ( - timer *time.Timer - expired <-chan time.Time - resetChan = i.resetChan - setTimeoutChan = i.setTimeoutChan - ) -Loop: - for { - select { - case timeout := <-i.setTimeoutChan: - i.timeout = timeout - if timeout == 0 { - if timer != nil { - timer.Stop() - } - } else { - if timer == nil { - timer = time.NewTimer(timeout) - expired = timer.C - } else { - timer.Reset(timeout) - } - } - case <-resetChan: - if timer != nil && i.timeout > 0 { - timer.Reset(i.timeout) - } - case <-expired: - i.conn.streamCond.L.Lock() - streams := i.conn.streams - i.conn.streams = make(map[spdy.StreamId]*Stream) - i.conn.streamCond.Broadcast() - i.conn.streamCond.L.Unlock() - go func() { - for _, stream := range streams { - stream.resetStream() - } - i.conn.Close() - }() - case <-i.conn.closeChan: - if timer != nil { - timer.Stop() - } - - // Start a goroutine to drain resetChan. This is needed because we've seen - // some unit tests with large numbers of goroutines get into a situation - // where resetChan fills up, at least 1 call to Write() is still trying to - // send to resetChan, the connection gets closed, and this case statement - // attempts to grab the write lock that Write() already has, causing a - // deadlock. - // - // See https://github.com/moby/spdystream/issues/49 for more details. - go func() { - for range resetChan { - } - }() - - go func() { - for range setTimeoutChan { - } - }() - - i.writeLock.Lock() - close(resetChan) - i.resetChan = nil - i.writeLock.Unlock() - - i.setTimeoutLock.Lock() - close(i.setTimeoutChan) - i.setTimeoutChan = nil - i.setTimeoutLock.Unlock() - - break Loop - } - } - - // Drain resetChan - for range resetChan { - } -} - -func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error { - i.writeLock.Lock() - defer i.writeLock.Unlock() - if i.resetChan == nil { - return io.EOF - } - err := i.f.WriteFrame(frame) - if err != nil { - return err - } - - i.resetChan <- struct{}{} - - return nil -} - -func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) { - frame, err := i.f.ReadFrame() - if err != nil { - return nil, err - } - - // resetChan should never be closed since it is only closed - // when the connection has closed its closeChan. This closure - // only occurs after all Reads have finished - // TODO (dmcgowan): refactor relationship into connection - i.resetChan <- struct{}{} - - return frame, nil -} - -func (i *idleAwareFramer) setIdleTimeout(timeout time.Duration) { - i.setTimeoutLock.Lock() - defer i.setTimeoutLock.Unlock() - - if i.setTimeoutChan == nil { - return - } - - i.setTimeoutChan <- timeout -} - -type Connection struct { - conn net.Conn - framer *idleAwareFramer - - closeChan chan bool - goneAway bool - lastStreamChan chan<- *Stream - goAwayTimeout time.Duration - closeTimeout time.Duration - - streamLock *sync.RWMutex - streamCond *sync.Cond - streams map[spdy.StreamId]*Stream - - nextIdLock sync.Mutex - receiveIdLock sync.Mutex - nextStreamId spdy.StreamId - receivedStreamId spdy.StreamId - - // pingLock protects pingChans and pingId - pingLock sync.Mutex - pingId uint32 - pingChans map[uint32]chan error - - shutdownLock sync.Mutex - shutdownChan chan error - hasShutdown bool - - // for testing https://github.com/moby/spdystream/pull/56 - dataFrameHandler func(*spdy.DataFrame) error -} - -// NewConnection creates a new spdy connection from an existing -// network connection. -func NewConnection(conn net.Conn, server bool) (*Connection, error) { - framer, framerErr := spdy.NewFramer(conn, conn) - if framerErr != nil { - return nil, framerErr - } - idleAwareFramer := newIdleAwareFramer(framer) - var sid spdy.StreamId - var rid spdy.StreamId - var pid uint32 - if server { - sid = 2 - rid = 1 - pid = 2 - } else { - sid = 1 - rid = 2 - pid = 1 - } - - streamLock := new(sync.RWMutex) - streamCond := sync.NewCond(streamLock) - - session := &Connection{ - conn: conn, - framer: idleAwareFramer, - - closeChan: make(chan bool), - goAwayTimeout: time.Duration(0), - closeTimeout: time.Duration(0), - - streamLock: streamLock, - streamCond: streamCond, - streams: make(map[spdy.StreamId]*Stream), - nextStreamId: sid, - receivedStreamId: rid, - - pingId: pid, - pingChans: make(map[uint32]chan error), - - shutdownChan: make(chan error), - } - session.dataFrameHandler = session.handleDataFrame - idleAwareFramer.conn = session - go idleAwareFramer.monitor() - - return session, nil -} - -// Ping sends a ping frame across the connection and -// returns the response time -func (s *Connection) Ping() (time.Duration, error) { - pid := s.pingId - s.pingLock.Lock() - if s.pingId > 0x7ffffffe { - s.pingId = s.pingId - 0x7ffffffe - } else { - s.pingId = s.pingId + 2 - } - pingChan := make(chan error) - s.pingChans[pid] = pingChan - s.pingLock.Unlock() - defer func() { - s.pingLock.Lock() - delete(s.pingChans, pid) - s.pingLock.Unlock() - }() - - frame := &spdy.PingFrame{Id: pid} - startTime := time.Now() - writeErr := s.framer.WriteFrame(frame) - if writeErr != nil { - return time.Duration(0), writeErr - } - select { - case <-s.closeChan: - return time.Duration(0), errors.New("connection closed") - case err, ok := <-pingChan: - if ok && err != nil { - return time.Duration(0), err - } - break - } - return time.Since(startTime), nil -} - -// Serve handles frames sent from the server, including reply frames -// which are needed to fully initiate connections. Both clients and servers -// should call Serve in a separate goroutine before creating streams. -func (s *Connection) Serve(newHandler StreamHandler) { - // use a WaitGroup to wait for all frames to be drained after receiving - // go-away. - var wg sync.WaitGroup - - // Parition queues to ensure stream frames are handled - // by the same worker, ensuring order is maintained - frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS) - for i := 0; i < FRAME_WORKERS; i++ { - frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE) - - // Ensure frame queue is drained when connection is closed - go func(frameQueue *PriorityFrameQueue) { - <-s.closeChan - frameQueue.Drain() - }(frameQueues[i]) - - wg.Add(1) - go func(frameQueue *PriorityFrameQueue) { - // let the WaitGroup know this worker is done - defer wg.Done() - - s.frameHandler(frameQueue, newHandler) - }(frameQueues[i]) - } - - var ( - partitionRoundRobin int - goAwayFrame *spdy.GoAwayFrame - ) -Loop: - for { - readFrame, err := s.framer.ReadFrame() - if err != nil { - if err != io.EOF { - debugMessage("frame read error: %s", err) - } else { - debugMessage("(%p) EOF received", s) - } - break - } - var priority uint8 - var partition int - switch frame := readFrame.(type) { - case *spdy.SynStreamFrame: - if s.checkStreamFrame(frame) { - priority = frame.Priority - partition = int(frame.StreamId % FRAME_WORKERS) - debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId) - s.addStreamFrame(frame) - } else { - debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId) - continue - } - case *spdy.SynReplyFrame: - priority = s.getStreamPriority(frame.StreamId) - partition = int(frame.StreamId % FRAME_WORKERS) - case *spdy.DataFrame: - priority = s.getStreamPriority(frame.StreamId) - partition = int(frame.StreamId % FRAME_WORKERS) - case *spdy.RstStreamFrame: - priority = s.getStreamPriority(frame.StreamId) - partition = int(frame.StreamId % FRAME_WORKERS) - case *spdy.HeadersFrame: - priority = s.getStreamPriority(frame.StreamId) - partition = int(frame.StreamId % FRAME_WORKERS) - case *spdy.PingFrame: - priority = 0 - partition = partitionRoundRobin - partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS - case *spdy.GoAwayFrame: - // hold on to the go away frame and exit the loop - goAwayFrame = frame - break Loop - default: - priority = 7 - partition = partitionRoundRobin - partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS - } - frameQueues[partition].Push(readFrame, priority) - } - close(s.closeChan) - - // wait for all frame handler workers to indicate they've drained their queues - // before handling the go away frame - wg.Wait() - - if goAwayFrame != nil { - s.handleGoAwayFrame(goAwayFrame) - } - - // now it's safe to close remote channels and empty s.streams - s.streamCond.L.Lock() - // notify streams that they're now closed, which will - // unblock any stream Read() calls - for _, stream := range s.streams { - stream.closeRemoteChannels() - } - s.streams = make(map[spdy.StreamId]*Stream) - s.streamCond.Broadcast() - s.streamCond.L.Unlock() -} - -func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) { - for { - popFrame := frameQueue.Pop() - if popFrame == nil { - return - } - - var frameErr error - switch frame := popFrame.(type) { - case *spdy.SynStreamFrame: - frameErr = s.handleStreamFrame(frame, newHandler) - case *spdy.SynReplyFrame: - frameErr = s.handleReplyFrame(frame) - case *spdy.DataFrame: - frameErr = s.dataFrameHandler(frame) - case *spdy.RstStreamFrame: - frameErr = s.handleResetFrame(frame) - case *spdy.HeadersFrame: - frameErr = s.handleHeaderFrame(frame) - case *spdy.PingFrame: - frameErr = s.handlePingFrame(frame) - case *spdy.GoAwayFrame: - frameErr = s.handleGoAwayFrame(frame) - default: - frameErr = fmt.Errorf("unhandled frame type: %T", frame) - } - - if frameErr != nil { - debugMessage("frame handling error: %s", frameErr) - } - } -} - -func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 { - stream, streamOk := s.getStream(streamId) - if !streamOk { - return 7 - } - return stream.priority -} - -func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) { - var parent *Stream - if frame.AssociatedToStreamId != spdy.StreamId(0) { - parent, _ = s.getStream(frame.AssociatedToStreamId) - } - - stream := &Stream{ - streamId: frame.StreamId, - parent: parent, - conn: s, - startChan: make(chan error), - headers: frame.Headers, - finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00, - replyCond: sync.NewCond(new(sync.Mutex)), - dataChan: make(chan []byte), - headerChan: make(chan http.Header), - closeChan: make(chan bool), - priority: frame.Priority, - } - if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 { - stream.closeRemoteChannels() - } - - s.addStream(stream) -} - -// checkStreamFrame checks to see if a stream frame is allowed. -// If the stream is invalid, then a reset frame with protocol error -// will be returned. -func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool { - s.receiveIdLock.Lock() - defer s.receiveIdLock.Unlock() - if s.goneAway { - return false - } - validationErr := s.validateStreamId(frame.StreamId) - if validationErr != nil { - go func() { - resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId) - if resetErr != nil { - debugMessage("reset error: %s", resetErr) - } - }() - return false - } - return true -} - -func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error { - stream, ok := s.getStream(frame.StreamId) - if !ok { - return fmt.Errorf("Missing stream: %d", frame.StreamId) - } - - newHandler(stream) - - return nil -} - -func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error { - debugMessage("(%p) Reply frame received for %d", s, frame.StreamId) - stream, streamOk := s.getStream(frame.StreamId) - if !streamOk { - debugMessage("Reply frame gone away for %d", frame.StreamId) - // Stream has already gone away - return nil - } - if stream.replied { - // Stream has already received reply - return nil - } - stream.replied = true - - // TODO Check for error - if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { - s.remoteStreamFinish(stream) - } - - close(stream.startChan) - - return nil -} - -func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error { - stream, streamOk := s.getStream(frame.StreamId) - if !streamOk { - // Stream has already been removed - return nil - } - s.removeStream(stream) - stream.closeRemoteChannels() - - if !stream.replied { - stream.replied = true - stream.startChan <- ErrReset - close(stream.startChan) - } - - stream.finishLock.Lock() - stream.finished = true - stream.finishLock.Unlock() - - return nil -} - -func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error { - stream, streamOk := s.getStream(frame.StreamId) - if !streamOk { - // Stream has already gone away - return nil - } - if !stream.replied { - // No reply received...Protocol error? - return nil - } - - // TODO limit headers while not blocking (use buffered chan or goroutine?) - select { - case <-stream.closeChan: - return nil - case stream.headerChan <- frame.Headers: - } - - if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { - s.remoteStreamFinish(stream) - } - - return nil -} - -func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error { - debugMessage("(%p) Data frame received for %d", s, frame.StreamId) - stream, streamOk := s.getStream(frame.StreamId) - if !streamOk { - debugMessage("(%p) Data frame gone away for %d", s, frame.StreamId) - // Stream has already gone away - return nil - } - if !stream.replied { - debugMessage("(%p) Data frame not replied %d", s, frame.StreamId) - // No reply received...Protocol error? - return nil - } - - debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId) - if len(frame.Data) > 0 { - stream.dataLock.RLock() - select { - case <-stream.closeChan: - debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId) - case stream.dataChan <- frame.Data: - debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId) - } - stream.dataLock.RUnlock() - } - if (frame.Flags & spdy.DataFlagFin) != 0x00 { - s.remoteStreamFinish(stream) - } - return nil -} - -func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error { - s.pingLock.Lock() - pingId := s.pingId - pingChan, pingOk := s.pingChans[frame.Id] - s.pingLock.Unlock() - - if pingId&0x01 != frame.Id&0x01 { - return s.framer.WriteFrame(frame) - } - if pingOk { - close(pingChan) - } - return nil -} - -func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error { - debugMessage("(%p) Go away received", s) - s.receiveIdLock.Lock() - if s.goneAway { - s.receiveIdLock.Unlock() - return nil - } - s.goneAway = true - s.receiveIdLock.Unlock() - - if s.lastStreamChan != nil { - stream, _ := s.getStream(frame.LastGoodStreamId) - go func() { - s.lastStreamChan <- stream - }() - } - - // Do not block frame handler waiting for closure - go s.shutdown(s.goAwayTimeout) - - return nil -} - -func (s *Connection) remoteStreamFinish(stream *Stream) { - stream.closeRemoteChannels() - - stream.finishLock.Lock() - if stream.finished { - // Stream is fully closed, cleanup - s.removeStream(stream) - } - stream.finishLock.Unlock() -} - -// CreateStream creates a new spdy stream using the parameters for -// creating the stream frame. The stream frame will be sent upon -// calling this function, however this function does not wait for -// the reply frame. If waiting for the reply is desired, use -// the stream Wait or WaitTimeout function on the stream returned -// by this function. -func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) { - // MUST synchronize stream creation (all the way to writing the frame) - // as stream IDs **MUST** increase monotonically. - s.nextIdLock.Lock() - defer s.nextIdLock.Unlock() - - streamId := s.getNextStreamId() - if streamId == 0 { - return nil, fmt.Errorf("Unable to get new stream id") - } - - stream := &Stream{ - streamId: streamId, - parent: parent, - conn: s, - startChan: make(chan error), - headers: headers, - dataChan: make(chan []byte), - headerChan: make(chan http.Header), - closeChan: make(chan bool), - } - - debugMessage("(%p) (%p) Create stream", s, stream) - - s.addStream(stream) - - return stream, s.sendStream(stream, fin) -} - -func (s *Connection) shutdown(closeTimeout time.Duration) { - // TODO Ensure this isn't called multiple times - s.shutdownLock.Lock() - if s.hasShutdown { - s.shutdownLock.Unlock() - return - } - s.hasShutdown = true - s.shutdownLock.Unlock() - - var timeout <-chan time.Time - if closeTimeout > time.Duration(0) { - timer := time.NewTimer(closeTimeout) - defer timer.Stop() - timeout = timer.C - } - streamsClosed := make(chan bool) - - go func() { - s.streamCond.L.Lock() - for len(s.streams) > 0 { - debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams) - s.streamCond.Wait() - } - s.streamCond.L.Unlock() - close(streamsClosed) - }() - - var err error - select { - case <-streamsClosed: - // No active streams, close should be safe - err = s.conn.Close() - case <-timeout: - // Force ungraceful close - err = s.conn.Close() - // Wait for cleanup to clear active streams - <-streamsClosed - } - - if err != nil { - // default to 1 second - duration := time.Second - // if a closeTimeout was given, use that, clipped to 1s-10m - if closeTimeout > time.Second { - duration = closeTimeout - } - if duration > 10*time.Minute { - duration = 10 * time.Minute - } - timer := time.NewTimer(duration) - defer timer.Stop() - select { - case s.shutdownChan <- err: - // error was handled - case <-timer.C: - debugMessage("Unhandled close error after %s: %s", duration, err) - } - } - close(s.shutdownChan) -} - -// Closes spdy connection by sending GoAway frame and initiating shutdown -func (s *Connection) Close() error { - s.receiveIdLock.Lock() - if s.goneAway { - s.receiveIdLock.Unlock() - return nil - } - s.goneAway = true - s.receiveIdLock.Unlock() - - var lastStreamId spdy.StreamId - if s.receivedStreamId > 2 { - lastStreamId = s.receivedStreamId - 2 - } - - goAwayFrame := &spdy.GoAwayFrame{ - LastGoodStreamId: lastStreamId, - Status: spdy.GoAwayOK, - } - - err := s.framer.WriteFrame(goAwayFrame) - go s.shutdown(s.closeTimeout) - if err != nil { - return err - } - - return nil -} - -// CloseWait closes the connection and waits for shutdown -// to finish. Note the underlying network Connection -// is not closed until the end of shutdown. -func (s *Connection) CloseWait() error { - closeErr := s.Close() - if closeErr != nil { - return closeErr - } - shutdownErr, ok := <-s.shutdownChan - if ok { - return shutdownErr - } - return nil -} - -// Wait waits for the connection to finish shutdown or for -// the wait timeout duration to expire. This needs to be -// called either after Close has been called or the GOAWAYFRAME -// has been received. If the wait timeout is 0, this function -// will block until shutdown finishes. If wait is never called -// and a shutdown error occurs, that error will be logged as an -// unhandled error. -func (s *Connection) Wait(waitTimeout time.Duration) error { - var timeout <-chan time.Time - if waitTimeout > time.Duration(0) { - timer := time.NewTimer(waitTimeout) - defer timer.Stop() - timeout = timer.C - } - - select { - case err, ok := <-s.shutdownChan: - if ok { - return err - } - case <-timeout: - return ErrTimeout - } - return nil -} - -// NotifyClose registers a channel to be called when the remote -// peer inidicates connection closure. The last stream to be -// received by the remote will be sent on the channel. The notify -// timeout will determine the duration between go away received -// and the connection being closed. -func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) { - s.goAwayTimeout = timeout - s.lastStreamChan = c -} - -// SetCloseTimeout sets the amount of time close will wait for -// streams to finish before terminating the underlying network -// connection. Setting the timeout to 0 will cause close to -// wait forever, which is the default. -func (s *Connection) SetCloseTimeout(timeout time.Duration) { - s.closeTimeout = timeout -} - -// SetIdleTimeout sets the amount of time the connection may sit idle before -// it is forcefully terminated. -func (s *Connection) SetIdleTimeout(timeout time.Duration) { - s.framer.setIdleTimeout(timeout) -} - -func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error { - var flags spdy.ControlFlags - if fin { - flags = spdy.ControlFlagFin - } - - headerFrame := &spdy.HeadersFrame{ - StreamId: stream.streamId, - Headers: headers, - CFHeader: spdy.ControlFrameHeader{Flags: flags}, - } - - return s.framer.WriteFrame(headerFrame) -} - -func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error { - var flags spdy.ControlFlags - if fin { - flags = spdy.ControlFlagFin - } - - replyFrame := &spdy.SynReplyFrame{ - StreamId: stream.streamId, - Headers: headers, - CFHeader: spdy.ControlFrameHeader{Flags: flags}, - } - - return s.framer.WriteFrame(replyFrame) -} - -func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error { - resetFrame := &spdy.RstStreamFrame{ - StreamId: streamId, - Status: status, - } - - return s.framer.WriteFrame(resetFrame) -} - -func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error { - return s.sendResetFrame(status, stream.streamId) -} - -func (s *Connection) sendStream(stream *Stream, fin bool) error { - var flags spdy.ControlFlags - if fin { - flags = spdy.ControlFlagFin - stream.finished = true - } - - var parentId spdy.StreamId - if stream.parent != nil { - parentId = stream.parent.streamId - } - - streamFrame := &spdy.SynStreamFrame{ - StreamId: spdy.StreamId(stream.streamId), - AssociatedToStreamId: spdy.StreamId(parentId), - Headers: stream.headers, - CFHeader: spdy.ControlFrameHeader{Flags: flags}, - } - - return s.framer.WriteFrame(streamFrame) -} - -// getNextStreamId returns the next sequential id -// every call should produce a unique value or an error -func (s *Connection) getNextStreamId() spdy.StreamId { - sid := s.nextStreamId - if sid > 0x7fffffff { - return 0 - } - s.nextStreamId = s.nextStreamId + 2 - return sid -} - -// PeekNextStreamId returns the next sequential id and keeps the next id untouched -func (s *Connection) PeekNextStreamId() spdy.StreamId { - sid := s.nextStreamId - return sid -} - -func (s *Connection) validateStreamId(rid spdy.StreamId) error { - if rid > 0x7fffffff || rid < s.receivedStreamId { - return ErrInvalidStreamId - } - s.receivedStreamId = rid + 2 - return nil -} - -func (s *Connection) addStream(stream *Stream) { - s.streamCond.L.Lock() - s.streams[stream.streamId] = stream - debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId) - s.streamCond.Broadcast() - s.streamCond.L.Unlock() -} - -func (s *Connection) removeStream(stream *Stream) { - s.streamCond.L.Lock() - delete(s.streams, stream.streamId) - debugMessage("(%p) (%p) Stream removed, broadcasting: %d", s, stream, stream.streamId) - s.streamCond.Broadcast() - s.streamCond.L.Unlock() -} - -func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) { - s.streamLock.RLock() - stream, ok = s.streams[streamId] - s.streamLock.RUnlock() - return -} - -// FindStream looks up the given stream id and either waits for the -// stream to be found or returns nil if the stream id is no longer -// valid. -func (s *Connection) FindStream(streamId uint32) *Stream { - var stream *Stream - var ok bool - s.streamCond.L.Lock() - stream, ok = s.streams[spdy.StreamId(streamId)] - debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok) - for !ok && streamId >= uint32(s.receivedStreamId) { - s.streamCond.Wait() - stream, ok = s.streams[spdy.StreamId(streamId)] - } - s.streamCond.L.Unlock() - return stream -} - -func (s *Connection) CloseChan() <-chan bool { - return s.closeChan -} diff --git a/vendor/github.com/moby/spdystream/handlers.go b/vendor/github.com/moby/spdystream/handlers.go deleted file mode 100644 index d68f61f81..000000000 --- a/vendor/github.com/moby/spdystream/handlers.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package spdystream - -import ( - "io" - "net/http" -) - -// MirrorStreamHandler mirrors all streams. -func MirrorStreamHandler(stream *Stream) { - replyErr := stream.SendReply(http.Header{}, false) - if replyErr != nil { - return - } - - go func() { - io.Copy(stream, stream) - stream.Close() - }() - go func() { - for { - header, receiveErr := stream.ReceiveHeader() - if receiveErr != nil { - return - } - sendErr := stream.SendHeader(header, false) - if sendErr != nil { - return - } - } - }() -} - -// NoopStreamHandler does nothing when stream connects. -func NoOpStreamHandler(stream *Stream) { - stream.SendReply(http.Header{}, false) -} diff --git a/vendor/github.com/moby/spdystream/priority.go b/vendor/github.com/moby/spdystream/priority.go deleted file mode 100644 index d8eb3516c..000000000 --- a/vendor/github.com/moby/spdystream/priority.go +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package spdystream - -import ( - "container/heap" - "sync" - - "github.com/moby/spdystream/spdy" -) - -type prioritizedFrame struct { - frame spdy.Frame - priority uint8 - insertId uint64 -} - -type frameQueue []*prioritizedFrame - -func (fq frameQueue) Len() int { - return len(fq) -} - -func (fq frameQueue) Less(i, j int) bool { - if fq[i].priority == fq[j].priority { - return fq[i].insertId < fq[j].insertId - } - return fq[i].priority < fq[j].priority -} - -func (fq frameQueue) Swap(i, j int) { - fq[i], fq[j] = fq[j], fq[i] -} - -func (fq *frameQueue) Push(x interface{}) { - *fq = append(*fq, x.(*prioritizedFrame)) -} - -func (fq *frameQueue) Pop() interface{} { - old := *fq - n := len(old) - *fq = old[0 : n-1] - return old[n-1] -} - -type PriorityFrameQueue struct { - queue *frameQueue - c *sync.Cond - size int - nextInsertId uint64 - drain bool -} - -func NewPriorityFrameQueue(size int) *PriorityFrameQueue { - queue := make(frameQueue, 0, size) - heap.Init(&queue) - - return &PriorityFrameQueue{ - queue: &queue, - size: size, - c: sync.NewCond(&sync.Mutex{}), - } -} - -func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) { - q.c.L.Lock() - defer q.c.L.Unlock() - for q.queue.Len() >= q.size { - q.c.Wait() - } - pFrame := &prioritizedFrame{ - frame: frame, - priority: priority, - insertId: q.nextInsertId, - } - q.nextInsertId = q.nextInsertId + 1 - heap.Push(q.queue, pFrame) - q.c.Signal() -} - -func (q *PriorityFrameQueue) Pop() spdy.Frame { - q.c.L.Lock() - defer q.c.L.Unlock() - for q.queue.Len() == 0 { - if q.drain { - return nil - } - q.c.Wait() - } - frame := heap.Pop(q.queue).(*prioritizedFrame).frame - q.c.Signal() - return frame -} - -func (q *PriorityFrameQueue) Drain() { - q.c.L.Lock() - defer q.c.L.Unlock() - q.drain = true - q.c.Broadcast() -} diff --git a/vendor/github.com/moby/spdystream/spdy/dictionary.go b/vendor/github.com/moby/spdystream/spdy/dictionary.go deleted file mode 100644 index 392232f17..000000000 --- a/vendor/github.com/moby/spdystream/spdy/dictionary.go +++ /dev/null @@ -1,203 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -// headerDictionary is the dictionary sent to the zlib compressor/decompressor. -var headerDictionary = []byte{ - 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, - 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, - 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, - 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, - 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, - 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, - 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, - 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, - 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, - 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, - 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, - 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, - 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, - 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, - 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, - 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, - 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, - 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, - 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, - 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, - 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, - 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, - 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, - 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, - 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, - 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, - 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, - 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, - 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, - 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, - 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, - 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, - 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, - 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, - 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, - 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, - 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, - 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, - 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, - 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, - 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, - 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, - 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, - 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, - 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, - 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, - 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, - 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, - 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, - 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, - 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, - 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, - 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, - 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, - 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, - 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, - 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, - 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, - 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, - 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, - 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, - 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, - 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, - 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, - 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, - 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, - 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, - 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, - 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, - 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, - 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, - 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, - 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, - 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, - 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, - 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, - 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, - 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, - 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, - 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, - 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, - 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, - 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, - 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, - 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, - 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, - 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, - 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, - 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, - 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, - 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, - 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, - 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, - 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, - 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, - 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, - 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, - 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, - 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, - 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, - 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, - 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, - 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, - 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, - 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, -} diff --git a/vendor/github.com/moby/spdystream/spdy/read.go b/vendor/github.com/moby/spdystream/spdy/read.go deleted file mode 100644 index 75ea045b8..000000000 --- a/vendor/github.com/moby/spdystream/spdy/read.go +++ /dev/null @@ -1,364 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "compress/zlib" - "encoding/binary" - "io" - "net/http" - "strings" -) - -func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readSynStreamFrame(h, frame) -} - -func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readSynReplyFrame(h, frame) -} - -func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { - return err - } - if frame.Status == 0 { - return &Error{InvalidControlFrame, frame.StreamId} - } - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - return nil -} - -func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - var numSettings uint32 - if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { - return err - } - frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) - for i := uint32(0); i < numSettings; i++ { - if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { - return err - } - frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) - frame.FlagIdValues[i].Id &= 0xffffff - if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { - return err - } - } - return nil -} - -func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { - return err - } - if frame.Id == 0 { - return &Error{ZeroStreamId, 0} - } - if frame.CFHeader.Flags != 0 { - return &Error{InvalidControlFrame, StreamId(frame.Id)} - } - return nil -} - -func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { - return err - } - if frame.CFHeader.Flags != 0 { - return &Error{InvalidControlFrame, frame.LastGoodStreamId} - } - if frame.CFHeader.length != 8 { - return &Error{InvalidControlFrame, frame.LastGoodStreamId} - } - if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { - return err - } - return nil -} - -func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readHeadersFrame(h, frame) -} - -func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if frame.CFHeader.Flags != 0 { - return &Error{InvalidControlFrame, frame.StreamId} - } - if frame.CFHeader.length != 8 { - return &Error{InvalidControlFrame, frame.StreamId} - } - if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { - return err - } - return nil -} - -func newControlFrame(frameType ControlFrameType) (controlFrame, error) { - ctor, ok := cframeCtor[frameType] - if !ok { - return nil, &Error{Err: InvalidControlFrame} - } - return ctor(), nil -} - -var cframeCtor = map[ControlFrameType]func() controlFrame{ - TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, - TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, - TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, - TypeSettings: func() controlFrame { return new(SettingsFrame) }, - TypePing: func() controlFrame { return new(PingFrame) }, - TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, - TypeHeaders: func() controlFrame { return new(HeadersFrame) }, - TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, -} - -func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { - if f.headerDecompressor != nil { - f.headerReader.N = payloadSize - return nil - } - f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} - decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) - if err != nil { - return err - } - f.headerDecompressor = decompressor - return nil -} - -// ReadFrame reads SPDY encoded data and returns a decompressed Frame. -func (f *Framer) ReadFrame() (Frame, error) { - var firstWord uint32 - if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { - return nil, err - } - if firstWord&0x80000000 != 0 { - frameType := ControlFrameType(firstWord & 0xffff) - version := uint16(firstWord >> 16 & 0x7fff) - return f.parseControlFrame(version, frameType) - } - return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) -} - -func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { - var length uint32 - if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { - return nil, err - } - flags := ControlFlags((length & 0xff000000) >> 24) - length &= 0xffffff - header := ControlFrameHeader{version, frameType, flags, length} - cframe, err := newControlFrame(frameType) - if err != nil { - return nil, err - } - if err = cframe.read(header, f); err != nil { - return nil, err - } - return cframe, nil -} - -func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { - var numHeaders uint32 - if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { - return nil, err - } - var e error - h := make(http.Header, int(numHeaders)) - for i := 0; i < int(numHeaders); i++ { - var length uint32 - if err := binary.Read(r, binary.BigEndian, &length); err != nil { - return nil, err - } - nameBytes := make([]byte, length) - if _, err := io.ReadFull(r, nameBytes); err != nil { - return nil, err - } - name := string(nameBytes) - if name != strings.ToLower(name) { - e = &Error{UnlowercasedHeaderName, streamId} - name = strings.ToLower(name) - } - if h[name] != nil { - e = &Error{DuplicateHeaders, streamId} - } - if err := binary.Read(r, binary.BigEndian, &length); err != nil { - return nil, err - } - value := make([]byte, length) - if _, err := io.ReadFull(r, value); err != nil { - return nil, err - } - valueList := strings.Split(string(value), headerValueSeparator) - for _, v := range valueList { - h.Add(name, v) - } - } - if e != nil { - return h, e - } - return h, nil -} - -func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { - return err - } - if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { - return err - } - frame.Priority >>= 5 - if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - err := f.uncorkHeaderDecompressor(int64(h.length - 10)) - if err != nil { - return err - } - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - for h := range frame.Headers { - if invalidReqHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - return nil -} - -func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - err := f.uncorkHeaderDecompressor(int64(h.length - 4)) - if err != nil { - return err - } - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - for h := range frame.Headers { - if invalidRespHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - return nil -} - -func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - err := f.uncorkHeaderDecompressor(int64(h.length - 4)) - if err != nil { - return err - } - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - var invalidHeaders map[string]bool - if frame.StreamId%2 == 0 { - invalidHeaders = invalidReqHeaders - } else { - invalidHeaders = invalidRespHeaders - } - for h := range frame.Headers { - if invalidHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - return nil -} - -func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { - var length uint32 - if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { - return nil, err - } - var frame DataFrame - frame.StreamId = streamId - frame.Flags = DataFlags(length >> 24) - length &= 0xffffff - frame.Data = make([]byte, length) - if _, err := io.ReadFull(f.r, frame.Data); err != nil { - return nil, err - } - if frame.StreamId == 0 { - return nil, &Error{ZeroStreamId, 0} - } - return &frame, nil -} diff --git a/vendor/github.com/moby/spdystream/spdy/types.go b/vendor/github.com/moby/spdystream/spdy/types.go deleted file mode 100644 index a254a43ab..000000000 --- a/vendor/github.com/moby/spdystream/spdy/types.go +++ /dev/null @@ -1,291 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package spdy implements the SPDY protocol (currently SPDY/3), described in -// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. -package spdy - -import ( - "bytes" - "compress/zlib" - "io" - "net/http" -) - -// Version is the protocol version number that this package implements. -const Version = 3 - -// ControlFrameType stores the type field in a control frame header. -type ControlFrameType uint16 - -const ( - TypeSynStream ControlFrameType = 0x0001 - TypeSynReply ControlFrameType = 0x0002 - TypeRstStream ControlFrameType = 0x0003 - TypeSettings ControlFrameType = 0x0004 - TypePing ControlFrameType = 0x0006 - TypeGoAway ControlFrameType = 0x0007 - TypeHeaders ControlFrameType = 0x0008 - TypeWindowUpdate ControlFrameType = 0x0009 -) - -// ControlFlags are the flags that can be set on a control frame. -type ControlFlags uint8 - -const ( - ControlFlagFin ControlFlags = 0x01 - ControlFlagUnidirectional ControlFlags = 0x02 - ControlFlagSettingsClearSettings ControlFlags = 0x01 -) - -// DataFlags are the flags that can be set on a data frame. -type DataFlags uint8 - -const ( - DataFlagFin DataFlags = 0x01 -) - -// MaxDataLength is the maximum number of bytes that can be stored in one frame. -const MaxDataLength = 1<<24 - 1 - -// headerValueSepator separates multiple header values. -const headerValueSeparator = "\x00" - -// Frame is a single SPDY frame in its unpacked in-memory representation. Use -// Framer to read and write it. -type Frame interface { - write(f *Framer) error -} - -// ControlFrameHeader contains all the fields in a control frame header, -// in its unpacked in-memory representation. -type ControlFrameHeader struct { - // Note, high bit is the "Control" bit. - version uint16 // spdy version number - frameType ControlFrameType - Flags ControlFlags - length uint32 // length of data field -} - -type controlFrame interface { - Frame - read(h ControlFrameHeader, f *Framer) error -} - -// StreamId represents a 31-bit value identifying the stream. -type StreamId uint32 - -// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM -// frame. -type SynStreamFrame struct { - CFHeader ControlFrameHeader - StreamId StreamId - AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to - Priority uint8 // priority of this frame (3-bit) - Slot uint8 // index in the server's credential vector of the client certificate - Headers http.Header -} - -// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. -type SynReplyFrame struct { - CFHeader ControlFrameHeader - StreamId StreamId - Headers http.Header -} - -// RstStreamStatus represents the status that led to a RST_STREAM. -type RstStreamStatus uint32 - -const ( - ProtocolError RstStreamStatus = iota + 1 - InvalidStream - RefusedStream - UnsupportedVersion - Cancel - InternalError - FlowControlError - StreamInUse - StreamAlreadyClosed - InvalidCredentials - FrameTooLarge -) - -// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM -// frame. -type RstStreamFrame struct { - CFHeader ControlFrameHeader - StreamId StreamId - Status RstStreamStatus -} - -// SettingsFlag represents a flag in a SETTINGS frame. -type SettingsFlag uint8 - -const ( - FlagSettingsPersistValue SettingsFlag = 0x1 - FlagSettingsPersisted SettingsFlag = 0x2 -) - -// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. -type SettingsId uint32 - -const ( - SettingsUploadBandwidth SettingsId = iota + 1 - SettingsDownloadBandwidth - SettingsRoundTripTime - SettingsMaxConcurrentStreams - SettingsCurrentCwnd - SettingsDownloadRetransRate - SettingsInitialWindowSize - SettingsClientCretificateVectorSize -) - -// SettingsFlagIdValue is the unpacked, in-memory representation of the -// combined flag/id/value for a setting in a SETTINGS frame. -type SettingsFlagIdValue struct { - Flag SettingsFlag - Id SettingsId - Value uint32 -} - -// SettingsFrame is the unpacked, in-memory representation of a SPDY -// SETTINGS frame. -type SettingsFrame struct { - CFHeader ControlFrameHeader - FlagIdValues []SettingsFlagIdValue -} - -// PingFrame is the unpacked, in-memory representation of a PING frame. -type PingFrame struct { - CFHeader ControlFrameHeader - Id uint32 // unique id for this ping, from server is even, from client is odd. -} - -// GoAwayStatus represents the status in a GoAwayFrame. -type GoAwayStatus uint32 - -const ( - GoAwayOK GoAwayStatus = iota - GoAwayProtocolError - GoAwayInternalError -) - -// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. -type GoAwayFrame struct { - CFHeader ControlFrameHeader - LastGoodStreamId StreamId // last stream id which was accepted by sender - Status GoAwayStatus -} - -// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. -type HeadersFrame struct { - CFHeader ControlFrameHeader - StreamId StreamId - Headers http.Header -} - -// WindowUpdateFrame is the unpacked, in-memory representation of a -// WINDOW_UPDATE frame. -type WindowUpdateFrame struct { - CFHeader ControlFrameHeader - StreamId StreamId - DeltaWindowSize uint32 // additional number of bytes to existing window size -} - -// TODO: Implement credential frame and related methods. - -// DataFrame is the unpacked, in-memory representation of a DATA frame. -type DataFrame struct { - // Note, high bit is the "Control" bit. Should be 0 for data frames. - StreamId StreamId - Flags DataFlags - Data []byte // payload data of this frame -} - -// A SPDY specific error. -type ErrorCode string - -const ( - UnlowercasedHeaderName ErrorCode = "header was not lowercased" - DuplicateHeaders ErrorCode = "multiple headers with same name" - WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" - UnknownFrameType ErrorCode = "unknown frame type" - InvalidControlFrame ErrorCode = "invalid control frame" - InvalidDataFrame ErrorCode = "invalid data frame" - InvalidHeaderPresent ErrorCode = "frame contained invalid header" - ZeroStreamId ErrorCode = "stream id zero is disallowed" -) - -// Error contains both the type of error and additional values. StreamId is 0 -// if Error is not associated with a stream. -type Error struct { - Err ErrorCode - StreamId StreamId -} - -func (e *Error) Error() string { - return string(e.Err) -} - -var invalidReqHeaders = map[string]bool{ - "Connection": true, - "Host": true, - "Keep-Alive": true, - "Proxy-Connection": true, - "Transfer-Encoding": true, -} - -var invalidRespHeaders = map[string]bool{ - "Connection": true, - "Keep-Alive": true, - "Proxy-Connection": true, - "Transfer-Encoding": true, -} - -// Framer handles serializing/deserializing SPDY frames, including compressing/ -// decompressing payloads. -type Framer struct { - headerCompressionDisabled bool - w io.Writer - headerBuf *bytes.Buffer - headerCompressor *zlib.Writer - r io.Reader - headerReader io.LimitedReader - headerDecompressor io.ReadCloser -} - -// NewFramer allocates a new Framer for a given SPDY connection, represented by -// a io.Writer and io.Reader. Note that Framer will read and write individual fields -// from/to the Reader and Writer, so the caller should pass in an appropriately -// buffered implementation to optimize performance. -func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { - compressBuf := new(bytes.Buffer) - compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) - if err != nil { - return nil, err - } - framer := &Framer{ - w: w, - headerBuf: compressBuf, - headerCompressor: compressor, - r: r, - } - return framer, nil -} diff --git a/vendor/github.com/moby/spdystream/spdy/write.go b/vendor/github.com/moby/spdystream/spdy/write.go deleted file mode 100644 index ab6d91f3b..000000000 --- a/vendor/github.com/moby/spdystream/spdy/write.go +++ /dev/null @@ -1,334 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "encoding/binary" - "io" - "net/http" - "strings" -) - -func (frame *SynStreamFrame) write(f *Framer) error { - return f.writeSynStreamFrame(frame) -} - -func (frame *SynReplyFrame) write(f *Framer) error { - return f.writeSynReplyFrame(frame) -} - -func (frame *RstStreamFrame) write(f *Framer) (err error) { - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeRstStream - frame.CFHeader.Flags = 0 - frame.CFHeader.length = 8 - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if frame.Status == 0 { - return &Error{InvalidControlFrame, frame.StreamId} - } - if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { - return - } - return -} - -func (frame *SettingsFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSettings - frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { - return - } - for _, flagIdValue := range frame.FlagIdValues { - flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) - if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { - return - } - } - return -} - -func (frame *PingFrame) write(f *Framer) (err error) { - if frame.Id == 0 { - return &Error{ZeroStreamId, 0} - } - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypePing - frame.CFHeader.Flags = 0 - frame.CFHeader.length = 4 - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { - return - } - return -} - -func (frame *GoAwayFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeGoAway - frame.CFHeader.Flags = 0 - frame.CFHeader.length = 8 - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { - return - } - return nil -} - -func (frame *HeadersFrame) write(f *Framer) error { - return f.writeHeadersFrame(frame) -} - -func (frame *WindowUpdateFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeWindowUpdate - frame.CFHeader.Flags = 0 - frame.CFHeader.length = 8 - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { - return - } - return nil -} - -func (frame *DataFrame) write(f *Framer) error { - return f.writeDataFrame(frame) -} - -// WriteFrame writes a frame. -func (f *Framer) WriteFrame(frame Frame) error { - return frame.write(f) -} - -func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { - if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { - return err - } - if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { - return err - } - flagsAndLength := uint32(h.Flags)<<24 | h.length - if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { - return err - } - return nil -} - -func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { - n = 0 - if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { - return - } - n += 2 - for name, values := range h { - if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { - return - } - n += 2 - name = strings.ToLower(name) - if _, err = io.WriteString(w, name); err != nil { - return - } - n += len(name) - v := strings.Join(values, headerValueSeparator) - if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { - return - } - n += 2 - if _, err = io.WriteString(w, v); err != nil { - return - } - n += len(v) - } - return -} - -func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader. - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSynStream - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { - return err - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return err - } - f.headerBuf.Reset() - return nil -} - -func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader. - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSynReply - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return - } - f.headerBuf.Reset() - return -} - -func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader. - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeHeaders - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) - - // Serialize frame to Writer. - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return - } - f.headerBuf.Reset() - return -} - -func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { - if frame.StreamId == 0 { - return &Error{ZeroStreamId, 0} - } - if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { - return &Error{InvalidDataFrame, frame.StreamId} - } - - // Serialize frame to Writer. - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) - if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { - return - } - if _, err = f.w.Write(frame.Data); err != nil { - return - } - return nil -} diff --git a/vendor/github.com/moby/spdystream/stream.go b/vendor/github.com/moby/spdystream/stream.go deleted file mode 100644 index 171c1e9e3..000000000 --- a/vendor/github.com/moby/spdystream/stream.go +++ /dev/null @@ -1,345 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package spdystream - -import ( - "errors" - "fmt" - "io" - "net" - "net/http" - "sync" - "time" - - "github.com/moby/spdystream/spdy" -) - -var ( - ErrUnreadPartialData = errors.New("unread partial data") -) - -type Stream struct { - streamId spdy.StreamId - parent *Stream - conn *Connection - startChan chan error - - dataLock sync.RWMutex - dataChan chan []byte - unread []byte - - priority uint8 - headers http.Header - headerChan chan http.Header - finishLock sync.Mutex - finished bool - replyCond *sync.Cond - replied bool - closeLock sync.Mutex - closeChan chan bool -} - -// WriteData writes data to stream, sending a dataframe per call -func (s *Stream) WriteData(data []byte, fin bool) error { - s.waitWriteReply() - var flags spdy.DataFlags - - if fin { - flags = spdy.DataFlagFin - s.finishLock.Lock() - if s.finished { - s.finishLock.Unlock() - return ErrWriteClosedStream - } - s.finished = true - s.finishLock.Unlock() - } - - dataFrame := &spdy.DataFrame{ - StreamId: s.streamId, - Flags: flags, - Data: data, - } - - debugMessage("(%p) (%d) Writing data frame", s, s.streamId) - return s.conn.framer.WriteFrame(dataFrame) -} - -// Write writes bytes to a stream, calling write data for each call. -func (s *Stream) Write(data []byte) (n int, err error) { - err = s.WriteData(data, false) - if err == nil { - n = len(data) - } - return -} - -// Read reads bytes from a stream, a single read will never get more -// than what is sent on a single data frame, but a multiple calls to -// read may get data from the same data frame. -func (s *Stream) Read(p []byte) (n int, err error) { - if s.unread == nil { - select { - case <-s.closeChan: - return 0, io.EOF - case read, ok := <-s.dataChan: - if !ok { - return 0, io.EOF - } - s.unread = read - } - } - n = copy(p, s.unread) - if n < len(s.unread) { - s.unread = s.unread[n:] - } else { - s.unread = nil - } - return -} - -// ReadData reads an entire data frame and returns the byte array -// from the data frame. If there is unread data from the result -// of a Read call, this function will return an ErrUnreadPartialData. -func (s *Stream) ReadData() ([]byte, error) { - debugMessage("(%p) Reading data from %d", s, s.streamId) - if s.unread != nil { - return nil, ErrUnreadPartialData - } - select { - case <-s.closeChan: - return nil, io.EOF - case read, ok := <-s.dataChan: - if !ok { - return nil, io.EOF - } - return read, nil - } -} - -func (s *Stream) waitWriteReply() { - if s.replyCond != nil { - s.replyCond.L.Lock() - for !s.replied { - s.replyCond.Wait() - } - s.replyCond.L.Unlock() - } -} - -// Wait waits for the stream to receive a reply. -func (s *Stream) Wait() error { - return s.WaitTimeout(time.Duration(0)) -} - -// WaitTimeout waits for the stream to receive a reply or for timeout. -// When the timeout is reached, ErrTimeout will be returned. -func (s *Stream) WaitTimeout(timeout time.Duration) error { - var timeoutChan <-chan time.Time - if timeout > time.Duration(0) { - timeoutChan = time.After(timeout) - } - - select { - case err := <-s.startChan: - if err != nil { - return err - } - break - case <-timeoutChan: - return ErrTimeout - } - return nil -} - -// Close closes the stream by sending an empty data frame with the -// finish flag set, indicating this side is finished with the stream. -func (s *Stream) Close() error { - select { - case <-s.closeChan: - // Stream is now fully closed - s.conn.removeStream(s) - default: - break - } - return s.WriteData([]byte{}, true) -} - -// Reset sends a reset frame, putting the stream into the fully closed state. -func (s *Stream) Reset() error { - s.conn.removeStream(s) - return s.resetStream() -} - -func (s *Stream) resetStream() error { - // Always call closeRemoteChannels, even if s.finished is already true. - // This makes it so that stream.Close() followed by stream.Reset() allows - // stream.Read() to unblock. - s.closeRemoteChannels() - - s.finishLock.Lock() - if s.finished { - s.finishLock.Unlock() - return nil - } - s.finished = true - s.finishLock.Unlock() - - resetFrame := &spdy.RstStreamFrame{ - StreamId: s.streamId, - Status: spdy.Cancel, - } - return s.conn.framer.WriteFrame(resetFrame) -} - -// CreateSubStream creates a stream using the current as the parent -func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) { - return s.conn.CreateStream(headers, s, fin) -} - -// SetPriority sets the stream priority, does not affect the -// remote priority of this stream after Open has been called. -// Valid values are 0 through 7, 0 being the highest priority -// and 7 the lowest. -func (s *Stream) SetPriority(priority uint8) { - s.priority = priority -} - -// SendHeader sends a header frame across the stream -func (s *Stream) SendHeader(headers http.Header, fin bool) error { - return s.conn.sendHeaders(headers, s, fin) -} - -// SendReply sends a reply on a stream, only valid to be called once -// when handling a new stream -func (s *Stream) SendReply(headers http.Header, fin bool) error { - if s.replyCond == nil { - return errors.New("cannot reply on initiated stream") - } - s.replyCond.L.Lock() - defer s.replyCond.L.Unlock() - if s.replied { - return nil - } - - err := s.conn.sendReply(headers, s, fin) - if err != nil { - return err - } - - s.replied = true - s.replyCond.Broadcast() - return nil -} - -// Refuse sends a reset frame with the status refuse, only -// valid to be called once when handling a new stream. This -// may be used to indicate that a stream is not allowed -// when http status codes are not being used. -func (s *Stream) Refuse() error { - if s.replied { - return nil - } - s.replied = true - return s.conn.sendReset(spdy.RefusedStream, s) -} - -// Cancel sends a reset frame with the status canceled. This -// can be used at any time by the creator of the Stream to -// indicate the stream is no longer needed. -func (s *Stream) Cancel() error { - return s.conn.sendReset(spdy.Cancel, s) -} - -// ReceiveHeader receives a header sent on the other side -// of the stream. This function will block until a header -// is received or stream is closed. -func (s *Stream) ReceiveHeader() (http.Header, error) { - select { - case <-s.closeChan: - break - case header, ok := <-s.headerChan: - if !ok { - return nil, fmt.Errorf("header chan closed") - } - return header, nil - } - return nil, fmt.Errorf("stream closed") -} - -// Parent returns the parent stream -func (s *Stream) Parent() *Stream { - return s.parent -} - -// Headers returns the headers used to create the stream -func (s *Stream) Headers() http.Header { - return s.headers -} - -// String returns the string version of stream using the -// streamId to uniquely identify the stream -func (s *Stream) String() string { - return fmt.Sprintf("stream:%d", s.streamId) -} - -// Identifier returns a 32 bit identifier for the stream -func (s *Stream) Identifier() uint32 { - return uint32(s.streamId) -} - -// IsFinished returns whether the stream has finished -// sending data -func (s *Stream) IsFinished() bool { - s.finishLock.Lock() - defer s.finishLock.Unlock() - return s.finished -} - -// Implement net.Conn interface - -func (s *Stream) LocalAddr() net.Addr { - return s.conn.conn.LocalAddr() -} - -func (s *Stream) RemoteAddr() net.Addr { - return s.conn.conn.RemoteAddr() -} - -// TODO set per stream values instead of connection-wide - -func (s *Stream) SetDeadline(t time.Time) error { - return s.conn.conn.SetDeadline(t) -} - -func (s *Stream) SetReadDeadline(t time.Time) error { - return s.conn.conn.SetReadDeadline(t) -} - -func (s *Stream) SetWriteDeadline(t time.Time) error { - return s.conn.conn.SetWriteDeadline(t) -} - -func (s *Stream) closeRemoteChannels() { - s.closeLock.Lock() - defer s.closeLock.Unlock() - select { - case <-s.closeChan: - default: - close(s.closeChan) - } -} diff --git a/vendor/github.com/moby/spdystream/utils.go b/vendor/github.com/moby/spdystream/utils.go deleted file mode 100644 index e9f7fffd6..000000000 --- a/vendor/github.com/moby/spdystream/utils.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright 2014-2021 Docker Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package spdystream - -import ( - "log" - "os" -) - -var ( - DEBUG = os.Getenv("DEBUG") -) - -func debugMessage(fmt string, args ...interface{}) { - if DEBUG != "" { - log.Printf(fmt, args...) - } -} diff --git a/vendor/github.com/mxk/go-flowrate/LICENSE b/vendor/github.com/mxk/go-flowrate/LICENSE deleted file mode 100644 index e9f9f628b..000000000 --- a/vendor/github.com/mxk/go-flowrate/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2014 The Go-FlowRate Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. - - * Neither the name of the go-flowrate project nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/mxk/go-flowrate/flowrate/flowrate.go b/vendor/github.com/mxk/go-flowrate/flowrate/flowrate.go deleted file mode 100644 index 1b727721e..000000000 --- a/vendor/github.com/mxk/go-flowrate/flowrate/flowrate.go +++ /dev/null @@ -1,267 +0,0 @@ -// -// Written by Maxim Khitrov (November 2012) -// - -// Package flowrate provides the tools for monitoring and limiting the flow rate -// of an arbitrary data stream. -package flowrate - -import ( - "math" - "sync" - "time" -) - -// Monitor monitors and limits the transfer rate of a data stream. -type Monitor struct { - mu sync.Mutex // Mutex guarding access to all internal fields - active bool // Flag indicating an active transfer - start time.Duration // Transfer start time (clock() value) - bytes int64 // Total number of bytes transferred - samples int64 // Total number of samples taken - - rSample float64 // Most recent transfer rate sample (bytes per second) - rEMA float64 // Exponential moving average of rSample - rPeak float64 // Peak transfer rate (max of all rSamples) - rWindow float64 // rEMA window (seconds) - - sBytes int64 // Number of bytes transferred since sLast - sLast time.Duration // Most recent sample time (stop time when inactive) - sRate time.Duration // Sampling rate - - tBytes int64 // Number of bytes expected in the current transfer - tLast time.Duration // Time of the most recent transfer of at least 1 byte -} - -// New creates a new flow control monitor. Instantaneous transfer rate is -// measured and updated for each sampleRate interval. windowSize determines the -// weight of each sample in the exponential moving average (EMA) calculation. -// The exact formulas are: -// -// sampleTime = currentTime - prevSampleTime -// sampleRate = byteCount / sampleTime -// weight = 1 - exp(-sampleTime/windowSize) -// newRate = weight*sampleRate + (1-weight)*oldRate -// -// The default values for sampleRate and windowSize (if <= 0) are 100ms and 1s, -// respectively. -func New(sampleRate, windowSize time.Duration) *Monitor { - if sampleRate = clockRound(sampleRate); sampleRate <= 0 { - sampleRate = 5 * clockRate - } - if windowSize <= 0 { - windowSize = 1 * time.Second - } - now := clock() - return &Monitor{ - active: true, - start: now, - rWindow: windowSize.Seconds(), - sLast: now, - sRate: sampleRate, - tLast: now, - } -} - -// Update records the transfer of n bytes and returns n. It should be called -// after each Read/Write operation, even if n is 0. -func (m *Monitor) Update(n int) int { - m.mu.Lock() - m.update(n) - m.mu.Unlock() - return n -} - -// IO is a convenience method intended to wrap io.Reader and io.Writer method -// execution. It calls m.Update(n) and then returns (n, err) unmodified. -func (m *Monitor) IO(n int, err error) (int, error) { - return m.Update(n), err -} - -// Done marks the transfer as finished and prevents any further updates or -// limiting. Instantaneous and current transfer rates drop to 0. Update, IO, and -// Limit methods become NOOPs. It returns the total number of bytes transferred. -func (m *Monitor) Done() int64 { - m.mu.Lock() - if now := m.update(0); m.sBytes > 0 { - m.reset(now) - } - m.active = false - m.tLast = 0 - n := m.bytes - m.mu.Unlock() - return n -} - -// timeRemLimit is the maximum Status.TimeRem value. -const timeRemLimit = 999*time.Hour + 59*time.Minute + 59*time.Second - -// Status represents the current Monitor status. All transfer rates are in bytes -// per second rounded to the nearest byte. -type Status struct { - Active bool // Flag indicating an active transfer - Start time.Time // Transfer start time - Duration time.Duration // Time period covered by the statistics - Idle time.Duration // Time since the last transfer of at least 1 byte - Bytes int64 // Total number of bytes transferred - Samples int64 // Total number of samples taken - InstRate int64 // Instantaneous transfer rate - CurRate int64 // Current transfer rate (EMA of InstRate) - AvgRate int64 // Average transfer rate (Bytes / Duration) - PeakRate int64 // Maximum instantaneous transfer rate - BytesRem int64 // Number of bytes remaining in the transfer - TimeRem time.Duration // Estimated time to completion - Progress Percent // Overall transfer progress -} - -// Status returns current transfer status information. The returned value -// becomes static after a call to Done. -func (m *Monitor) Status() Status { - m.mu.Lock() - now := m.update(0) - s := Status{ - Active: m.active, - Start: clockToTime(m.start), - Duration: m.sLast - m.start, - Idle: now - m.tLast, - Bytes: m.bytes, - Samples: m.samples, - PeakRate: round(m.rPeak), - BytesRem: m.tBytes - m.bytes, - Progress: percentOf(float64(m.bytes), float64(m.tBytes)), - } - if s.BytesRem < 0 { - s.BytesRem = 0 - } - if s.Duration > 0 { - rAvg := float64(s.Bytes) / s.Duration.Seconds() - s.AvgRate = round(rAvg) - if s.Active { - s.InstRate = round(m.rSample) - s.CurRate = round(m.rEMA) - if s.BytesRem > 0 { - if tRate := 0.8*m.rEMA + 0.2*rAvg; tRate > 0 { - ns := float64(s.BytesRem) / tRate * 1e9 - if ns > float64(timeRemLimit) { - ns = float64(timeRemLimit) - } - s.TimeRem = clockRound(time.Duration(ns)) - } - } - } - } - m.mu.Unlock() - return s -} - -// Limit restricts the instantaneous (per-sample) data flow to rate bytes per -// second. It returns the maximum number of bytes (0 <= n <= want) that may be -// transferred immediately without exceeding the limit. If block == true, the -// call blocks until n > 0. want is returned unmodified if want < 1, rate < 1, -// or the transfer is inactive (after a call to Done). -// -// At least one byte is always allowed to be transferred in any given sampling -// period. Thus, if the sampling rate is 100ms, the lowest achievable flow rate -// is 10 bytes per second. -// -// For usage examples, see the implementation of Reader and Writer in io.go. -func (m *Monitor) Limit(want int, rate int64, block bool) (n int) { - if want < 1 || rate < 1 { - return want - } - m.mu.Lock() - - // Determine the maximum number of bytes that can be sent in one sample - limit := round(float64(rate) * m.sRate.Seconds()) - if limit <= 0 { - limit = 1 - } - - // If block == true, wait until m.sBytes < limit - if now := m.update(0); block { - for m.sBytes >= limit && m.active { - now = m.waitNextSample(now) - } - } - - // Make limit <= want (unlimited if the transfer is no longer active) - if limit -= m.sBytes; limit > int64(want) || !m.active { - limit = int64(want) - } - m.mu.Unlock() - - if limit < 0 { - limit = 0 - } - return int(limit) -} - -// SetTransferSize specifies the total size of the data transfer, which allows -// the Monitor to calculate the overall progress and time to completion. -func (m *Monitor) SetTransferSize(bytes int64) { - if bytes < 0 { - bytes = 0 - } - m.mu.Lock() - m.tBytes = bytes - m.mu.Unlock() -} - -// update accumulates the transferred byte count for the current sample until -// clock() - m.sLast >= m.sRate. The monitor status is updated once the current -// sample is done. -func (m *Monitor) update(n int) (now time.Duration) { - if !m.active { - return - } - if now = clock(); n > 0 { - m.tLast = now - } - m.sBytes += int64(n) - if sTime := now - m.sLast; sTime >= m.sRate { - t := sTime.Seconds() - if m.rSample = float64(m.sBytes) / t; m.rSample > m.rPeak { - m.rPeak = m.rSample - } - - // Exponential moving average using a method similar to *nix load - // average calculation. Longer sampling periods carry greater weight. - if m.samples > 0 { - w := math.Exp(-t / m.rWindow) - m.rEMA = m.rSample + w*(m.rEMA-m.rSample) - } else { - m.rEMA = m.rSample - } - m.reset(now) - } - return -} - -// reset clears the current sample state in preparation for the next sample. -func (m *Monitor) reset(sampleTime time.Duration) { - m.bytes += m.sBytes - m.samples++ - m.sBytes = 0 - m.sLast = sampleTime -} - -// waitNextSample sleeps for the remainder of the current sample. The lock is -// released and reacquired during the actual sleep period, so it's possible for -// the transfer to be inactive when this method returns. -func (m *Monitor) waitNextSample(now time.Duration) time.Duration { - const minWait = 5 * time.Millisecond - current := m.sLast - - // sleep until the last sample time changes (ideally, just one iteration) - for m.sLast == current && m.active { - d := current + m.sRate - now - m.mu.Unlock() - if d < minWait { - d = minWait - } - time.Sleep(d) - m.mu.Lock() - now = m.update(0) - } - return now -} diff --git a/vendor/github.com/mxk/go-flowrate/flowrate/io.go b/vendor/github.com/mxk/go-flowrate/flowrate/io.go deleted file mode 100644 index fbe090972..000000000 --- a/vendor/github.com/mxk/go-flowrate/flowrate/io.go +++ /dev/null @@ -1,133 +0,0 @@ -// -// Written by Maxim Khitrov (November 2012) -// - -package flowrate - -import ( - "errors" - "io" -) - -// ErrLimit is returned by the Writer when a non-blocking write is short due to -// the transfer rate limit. -var ErrLimit = errors.New("flowrate: flow rate limit exceeded") - -// Limiter is implemented by the Reader and Writer to provide a consistent -// interface for monitoring and controlling data transfer. -type Limiter interface { - Done() int64 - Status() Status - SetTransferSize(bytes int64) - SetLimit(new int64) (old int64) - SetBlocking(new bool) (old bool) -} - -// Reader implements io.ReadCloser with a restriction on the rate of data -// transfer. -type Reader struct { - io.Reader // Data source - *Monitor // Flow control monitor - - limit int64 // Rate limit in bytes per second (unlimited when <= 0) - block bool // What to do when no new bytes can be read due to the limit -} - -// NewReader restricts all Read operations on r to limit bytes per second. -func NewReader(r io.Reader, limit int64) *Reader { - return &Reader{r, New(0, 0), limit, true} -} - -// Read reads up to len(p) bytes into p without exceeding the current transfer -// rate limit. It returns (0, nil) immediately if r is non-blocking and no new -// bytes can be read at this time. -func (r *Reader) Read(p []byte) (n int, err error) { - p = p[:r.Limit(len(p), r.limit, r.block)] - if len(p) > 0 { - n, err = r.IO(r.Reader.Read(p)) - } - return -} - -// SetLimit changes the transfer rate limit to new bytes per second and returns -// the previous setting. -func (r *Reader) SetLimit(new int64) (old int64) { - old, r.limit = r.limit, new - return -} - -// SetBlocking changes the blocking behavior and returns the previous setting. A -// Read call on a non-blocking reader returns immediately if no additional bytes -// may be read at this time due to the rate limit. -func (r *Reader) SetBlocking(new bool) (old bool) { - old, r.block = r.block, new - return -} - -// Close closes the underlying reader if it implements the io.Closer interface. -func (r *Reader) Close() error { - defer r.Done() - if c, ok := r.Reader.(io.Closer); ok { - return c.Close() - } - return nil -} - -// Writer implements io.WriteCloser with a restriction on the rate of data -// transfer. -type Writer struct { - io.Writer // Data destination - *Monitor // Flow control monitor - - limit int64 // Rate limit in bytes per second (unlimited when <= 0) - block bool // What to do when no new bytes can be written due to the limit -} - -// NewWriter restricts all Write operations on w to limit bytes per second. The -// transfer rate and the default blocking behavior (true) can be changed -// directly on the returned *Writer. -func NewWriter(w io.Writer, limit int64) *Writer { - return &Writer{w, New(0, 0), limit, true} -} - -// Write writes len(p) bytes from p to the underlying data stream without -// exceeding the current transfer rate limit. It returns (n, ErrLimit) if w is -// non-blocking and no additional bytes can be written at this time. -func (w *Writer) Write(p []byte) (n int, err error) { - var c int - for len(p) > 0 && err == nil { - s := p[:w.Limit(len(p), w.limit, w.block)] - if len(s) > 0 { - c, err = w.IO(w.Writer.Write(s)) - } else { - return n, ErrLimit - } - p = p[c:] - n += c - } - return -} - -// SetLimit changes the transfer rate limit to new bytes per second and returns -// the previous setting. -func (w *Writer) SetLimit(new int64) (old int64) { - old, w.limit = w.limit, new - return -} - -// SetBlocking changes the blocking behavior and returns the previous setting. A -// Write call on a non-blocking writer returns as soon as no additional bytes -// may be written at this time due to the rate limit. -func (w *Writer) SetBlocking(new bool) (old bool) { - old, w.block = w.block, new - return -} - -// Close closes the underlying writer if it implements the io.Closer interface. -func (w *Writer) Close() error { - defer w.Done() - if c, ok := w.Writer.(io.Closer); ok { - return c.Close() - } - return nil -} diff --git a/vendor/github.com/mxk/go-flowrate/flowrate/util.go b/vendor/github.com/mxk/go-flowrate/flowrate/util.go deleted file mode 100644 index 4caac583f..000000000 --- a/vendor/github.com/mxk/go-flowrate/flowrate/util.go +++ /dev/null @@ -1,67 +0,0 @@ -// -// Written by Maxim Khitrov (November 2012) -// - -package flowrate - -import ( - "math" - "strconv" - "time" -) - -// clockRate is the resolution and precision of clock(). -const clockRate = 20 * time.Millisecond - -// czero is the process start time rounded down to the nearest clockRate -// increment. -var czero = time.Duration(time.Now().UnixNano()) / clockRate * clockRate - -// clock returns a low resolution timestamp relative to the process start time. -func clock() time.Duration { - return time.Duration(time.Now().UnixNano())/clockRate*clockRate - czero -} - -// clockToTime converts a clock() timestamp to an absolute time.Time value. -func clockToTime(c time.Duration) time.Time { - return time.Unix(0, int64(czero+c)) -} - -// clockRound returns d rounded to the nearest clockRate increment. -func clockRound(d time.Duration) time.Duration { - return (d + clockRate>>1) / clockRate * clockRate -} - -// round returns x rounded to the nearest int64 (non-negative values only). -func round(x float64) int64 { - if _, frac := math.Modf(x); frac >= 0.5 { - return int64(math.Ceil(x)) - } - return int64(math.Floor(x)) -} - -// Percent represents a percentage in increments of 1/1000th of a percent. -type Percent uint32 - -// percentOf calculates what percent of the total is x. -func percentOf(x, total float64) Percent { - if x < 0 || total <= 0 { - return 0 - } else if p := round(x / total * 1e5); p <= math.MaxUint32 { - return Percent(p) - } - return Percent(math.MaxUint32) -} - -func (p Percent) Float() float64 { - return float64(p) * 1e-3 -} - -func (p Percent) String() string { - var buf [12]byte - b := strconv.AppendUint(buf[:0], uint64(p)/1000, 10) - n := len(b) - b = strconv.AppendUint(b, 1000+uint64(p)%1000, 10) - b[n] = '.' - return string(append(b, '%')) -} diff --git a/vendor/github.com/onsi/gomega/gcustom/make_matcher.go b/vendor/github.com/onsi/gomega/gcustom/make_matcher.go deleted file mode 100644 index 5372fa441..000000000 --- a/vendor/github.com/onsi/gomega/gcustom/make_matcher.go +++ /dev/null @@ -1,270 +0,0 @@ -/* -package gcustom provides a simple mechanism for creating custom Gomega matchers -*/ -package gcustom - -import ( - "fmt" - "reflect" - "strings" - "text/template" - - "github.com/onsi/gomega/format" -) - -var interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() -var errInterface = reflect.TypeOf((*error)(nil)).Elem() - -var defaultTemplate = template.Must(ParseTemplate("{{if .Failure}}Custom matcher failed for:{{else}}Custom matcher succeeded (but was expected to fail) for:{{end}}\n{{.FormattedActual}}")) - -func formatObject(object any, indent ...uint) string { - indentation := uint(0) - if len(indent) > 0 { - indentation = indent[0] - } - return format.Object(object, indentation) -} - -/* -ParseTemplate allows you to precompile templates for MakeMatcher's custom matchers. - -Use ParseTemplate if you are concerned about performance and would like to avoid repeatedly parsing failure message templates. The data made available to the template is documented in the WithTemplate() method of CustomGomegaMatcher. - -Once parsed you can pass the template in either as an argument to MakeMatcher(matchFunc,