diff --git a/.github/workflows/conformance-test.yml b/.github/workflows/conformance-test.yml deleted file mode 100644 index 8f29fb3f7..000000000 --- a/.github/workflows/conformance-test.yml +++ /dev/null @@ -1,130 +0,0 @@ -name: Conformance Test - -on: - push: - branches: - - master - - release-v2-dev - pull_request: - branches: - - master - - release-v2-dev - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - prepare: - name: Prepare - runs-on: buildjet-2vcpu-ubuntu-2204 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Go Env - id: go - uses: actions/setup-go@v4 - with: - go-version: "1.22" - - - name: Install kind - run: | - go install sigs.k8s.io/kind@v0.23.0 - - - name: Install Helm - run: | - curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 - chmod 700 get_helm.sh - ./get_helm.sh - - conformance-test: - timeout-minutes: 60 - needs: - - prepare - runs-on: buildjet-2vcpu-ubuntu-2204 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Setup Go Env - uses: actions/setup-go@v4 - with: - go-version: "1.22" - - - name: Login to Private Registry - uses: docker/login-action@v1 - with: - registry: hkccr.ccs.tencentyun.com - username: ${{ secrets.PRIVATE_DOCKER_USERNAME }} - password: ${{ secrets.PRIVATE_DOCKER_PASSWORD }} - - - name: Build images - env: - TAG: dev - ARCH: amd64 - ENABLE_PROXY: "false" - BASE_IMAGE_TAG: "debug" - run: | - echo "building images..." - make build-image - - - name: Launch Kind Cluster - run: | - make kind-up - - - name: Install And Run Cloud Provider KIND - run: | - go install sigs.k8s.io/cloud-provider-kind@latest - nohup cloud-provider-kind > /tmp/kind-loadbalancer.log 2>&1 & - - - name: Install Gateway API And CRDs - run: | - make install - - - name: Loading Docker Image to Kind Cluster - run: | - make kind-load-images - - - name: Install API7EE3 - run: | - make download-api7ee3-chart - - - name: Run Conformance Test - shell: bash - env: - API7_EE_LICENSE: ${{ secrets.API7_EE_LICENSE }} - continue-on-error: true - run: | - make conformance-test - - - name: Get Logs from api7-ingress-controller - shell: bash - run: | - export KUBECONFIG=/tmp/apisix-ingress-cluster.kubeconfig - kubectl logs -n apisix-conformance-test -l app=apisix-ingress-controller - - - name: Upload Gateway API Conformance Report - if: ${{ github.event_name == 'push' }} - uses: actions/upload-artifact@v4 - with: - name: apisix-ingress-controller-conformance-report.yaml - path: apisix-ingress-controller-conformance-report.yaml - - - name: Format Conformance Test Report - if: ${{ github.event_name == 'pull_request' }} - run: | - echo '# conformance test report' > report.md - echo '```yaml' >> report.md - cat apisix-ingress-controller-conformance-report.yaml >> report.md - echo '```' >> report.md - - - name: Report Conformance Test Result to PR Comment - if: ${{ github.event_name == 'pull_request' }} - uses: mshick/add-pr-comment@v2 - with: - message-id: '${{ matrix.target }}' - message-path: | - report.md diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml deleted file mode 100644 index e4e8aa522..000000000 --- a/.github/workflows/e2e-test.yml +++ /dev/null @@ -1,94 +0,0 @@ -name: E2E Test - -on: - push: - branches: - - master - - release-v2-dev - pull_request: - branches: - - master - - release-v2-dev - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - prepare: - name: Prepare - runs-on: buildjet-2vcpu-ubuntu-2204 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Go Env - id: go - uses: actions/setup-go@v4 - with: - go-version: "1.22" - - - name: Install kind - run: | - go install sigs.k8s.io/kind@v0.23.0 - - - name: Install Helm - run: | - curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 - chmod 700 get_helm.sh - ./get_helm.sh - - e2e-test: - needs: - - prepare - runs-on: buildjet-2vcpu-ubuntu-2204 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Setup Go Env - uses: actions/setup-go@v4 - with: - go-version: "1.22" - - - name: Login to Private Registry - uses: docker/login-action@v1 - with: - registry: hkccr.ccs.tencentyun.com - username: ${{ secrets.PRIVATE_DOCKER_USERNAME }} - password: ${{ secrets.PRIVATE_DOCKER_PASSWORD }} - - - name: Build images - env: - TAG: dev - ARCH: amd64 - ENABLE_PROXY: "false" - BASE_IMAGE_TAG: "debug" - run: | - echo "building images..." - make build-image - - - name: Launch Kind Cluster - run: | - make kind-up - - - name: Install Gateway API And CRDs - run: | - make install - - - name: Download API7EE3 Chart - run: | - make download-api7ee3-chart - - - name: Loading Docker Image to Kind Cluster - run: | - make kind-load-images - - - name: Run E2E test suite - shell: bash - env: - API7_EE_LICENSE: ${{ secrets.API7_EE_LICENSE }} - run: | - make e2e-test diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 74044fdf7..a9dddb523 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -27,7 +27,7 @@ on: - '.github/**' jobs: update_release_draft: - if: github.repository == 'api7/api7-ingress-controller' + if: github.repository == 'apache/apisix-ingress-controller' runs-on: buildjet-2vcpu-ubuntu-2204 steps: - name: Drafting release diff --git a/.gitignore b/.gitignore index 51599fbae..9961009dd 100644 --- a/.gitignore +++ b/.gitignore @@ -34,5 +34,3 @@ apisix-ingress-controller-conformance-report.yaml *.mdx .cursor/ .env - -charts/api7ee3 diff --git a/Makefile b/Makefile index 194ae9a0b..c27506f67 100644 --- a/Makefile +++ b/Makefile @@ -5,14 +5,13 @@ VERSION ?= 2.0.0 IMAGE_TAG ?= dev -IMG ?= api7/api7-ingress-controller:$(IMAGE_TAG) +IMG ?= apache/apisix-ingress-controller:$(IMAGE_TAG) # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.30.0 KIND_NAME ?= apisix-ingress-cluster GATEAY_API_VERSION ?= v1.2.0 -DASHBOARD_VERSION ?= dev TEST_TIMEOUT ?= 45m # CRD Reference Documentation @@ -105,23 +104,6 @@ test: manifests generate fmt vet envtest ## Run tests. .PHONY: kind-e2e-test kind-e2e-test: kind-up build-image kind-load-images e2e-test -# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. -.PHONY: e2e-test -e2e-test: - @kind get kubeconfig --name $(KIND_NAME) > $$KUBECONFIG - DASHBOARD_VERSION=$(DASHBOARD_VERSION) go test ./test/e2e/ -test.timeout=$(TEST_TIMEOUT) -v -ginkgo.v -ginkgo.focus="$(TEST_FOCUS)" - -.PHONY: download-api7ee3-chart -download-api7ee3-chart: - @helm repo add api7 https://charts.api7.ai || true - @helm repo update - @helm pull api7/api7ee3 --destination "$(shell helm env HELM_REPOSITORY_CACHE)" - @echo "Downloaded API7EE3 chart" - -.PHONY: conformance-test -conformance-test: - DASHBOARD_VERSION=$(DASHBOARD_VERSION) go test -v ./test/conformance -tags=conformance -timeout 60m - .PHONY: lint lint: sort-import golangci-lint ## Run golangci-lint linter $(GOLANGCI_LINT) run @@ -146,30 +128,15 @@ kind-down: .PHONY: kind-load-images kind-load-images: pull-infra-images kind-load-ingress-image - @kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev --name $(KIND_NAME) - @kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION) --name $(KIND_NAME) - @kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION) --name $(KIND_NAME) @kind load docker-image kennethreitz/httpbin:latest --name $(KIND_NAME) @kind load docker-image jmalloc/echo-server:latest --name $(KIND_NAME) -.PHONY: kind-load-gateway-image -kind-load-gateway-image: - @kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev --name $(KIND_NAME) - -.PHONY: kind-load-dashboard-images -kind-load-dashboard-images: - @kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION) --name $(KIND_NAME) - @kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION) --name $(KIND_NAME) - .PHONY: kind-load-ingress-image kind-load-ingress-image: @kind load docker-image $(IMG) --name $(KIND_NAME) .PHONY: pull-infra-images pull-infra-images: - @docker pull hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev - @docker pull hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION) - @docker pull hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION) @docker pull kennethreitz/httpbin:latest @docker pull jmalloc/echo-server:latest @@ -314,10 +281,6 @@ $(GOLANGCI_LINT): $(LOCALBIN) gofmt: ## Apply go fmt @gofmt -w -r 'interface{} -> any' . - @gofmt -w -r 'FIt -> It' test - @gofmt -w -r 'FContext -> Context' test - @gofmt -w -r 'FDescribe -> Describe' test - @gofmt -w -r 'FDescribeTable -> DescribeTable' test @go fmt ./... .PHONY: gofmt diff --git a/README.md b/README.md index 5fb2e5fbe..173295116 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Description -The APISIX Ingress Controller allows you to run the APISIX Gateway as a Kubernetes Ingress to handle inbound traffic for a Kubernetes cluster. It dynamically configures and manages the API7 Gateway using Gateway API resources. +The APISIX Ingress Controller allows you to run the APISIX Gateway as a Kubernetes Ingress to handle inbound traffic for a Kubernetes cluster. It dynamically configures and manages the APISIX Gateway using Gateway API resources. ## Document @@ -41,7 +41,7 @@ make install **Deploy the Manager to the cluster with the image specified by `IMG`:** ```sh -make deploy #IMG=api7/api7-ingress-controller:dev +make deploy #IMG=apache/apisix-ingress-controller:dev ``` > **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin @@ -66,7 +66,7 @@ Following are the steps to build the installer and distribute this project to us 1. Build the installer for the image built and published in the registry: ```sh -make build-installer # IMG=api7/api7-ingress-controller:dev +make build-installer # IMG=apache/apisix-ingress-controller:dev ``` NOTE: The makefile target mentioned above generates an 'install.yaml' diff --git a/api/dashboard/v1/doc.go b/api/dashboard/v1/doc.go deleted file mode 100644 index 1e6c250bd..000000000 --- a/api/dashboard/v1/doc.go +++ /dev/null @@ -1,13 +0,0 @@ -// 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 v1 diff --git a/api/dashboard/v1/plugin_types.go b/api/dashboard/v1/plugin_types.go deleted file mode 100644 index 6bf9dacf0..000000000 --- a/api/dashboard/v1/plugin_types.go +++ /dev/null @@ -1,205 +0,0 @@ -// 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 v1 - -import ( - "github.com/incubator4/go-resty-expr/expr" -) - -const ( - PluginProxyRewrite string = "proxy-rewrite" - PluginRedirect string = "redirect" - PluginResponseRewrite string = "response-rewrite" - PluginProxyMirror string = "proxy-mirror" -) - -// TrafficSplitConfig is the config of traffic-split plugin. -// +k8s:deepcopy-gen=true -type TrafficSplitConfig struct { - Rules []TrafficSplitConfigRule `json:"rules"` -} - -// TrafficSplitConfigRule is the rule config in traffic-split plugin config. -// +k8s:deepcopy-gen=true -type TrafficSplitConfigRule struct { - WeightedUpstreams []TrafficSplitConfigRuleWeightedUpstream `json:"weighted_upstreams"` -} - -// TrafficSplitConfigRuleWeightedUpstream is the weighted upstream config in -// the traffic split plugin rule. -// +k8s:deepcopy-gen=true -type TrafficSplitConfigRuleWeightedUpstream struct { - UpstreamID string `json:"upstream_id,omitempty"` - Upstream *Upstream `json:"upstream,omitempty"` - Weight int `json:"weight"` -} - -// IPRestrictConfig is the rule config for ip-restriction plugin. -// +k8s:deepcopy-gen=true -type IPRestrictConfig struct { - Allowlist []string `json:"whitelist,omitempty"` - Blocklist []string `json:"blacklist,omitempty"` -} - -// CorsConfig is the rule config for cors plugin. -// +k8s:deepcopy-gen=true -type CorsConfig struct { - AllowOrigins string `json:"allow_origins,omitempty"` - AllowMethods string `json:"allow_methods,omitempty"` - AllowHeaders string `json:"allow_headers,omitempty"` -} - -// CSRfConfig is the rule config for csrf plugin. -// +k8s:deepcopy-gen=true -type CSRFConfig struct { - Key string `json:"key"` -} - -// KeyAuthConsumerConfig is the rule config for key-auth plugin -// used in Consumer object. -// +k8s:deepcopy-gen=true -type KeyAuthConsumerConfig struct { - Key string `json:"key"` -} - -// KeyAuthRouteConfig is the rule config for key-auth plugin -// used in Route object. -type KeyAuthRouteConfig struct { - Header string `json:"header,omitempty"` -} - -// BasicAuthConsumerConfig is the rule config for basic-auth plugin -// used in Consumer object. -// +k8s:deepcopy-gen=true -type BasicAuthConsumerConfig struct { - Username string `json:"username"` - Password string `json:"password"` -} - -// JwtAuthConsumerConfig is the rule config for jwt-auth plugin -// used in Consumer object. -// +k8s:deepcopy-gen=true -type JwtAuthConsumerConfig struct { - Key string `json:"key" yaml:"key"` - Secret string `json:"secret,omitempty" yaml:"secret,omitempty"` - PublicKey string `json:"public_key,omitempty" yaml:"public_key,omitempty"` - PrivateKey string `json:"private_key" yaml:"private_key,omitempty"` - Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"` - Exp int64 `json:"exp,omitempty" yaml:"exp,omitempty"` - Base64Secret bool `json:"base64_secret,omitempty" yaml:"base64_secret,omitempty"` - LifetimeGracePeriod int64 `json:"lifetime_grace_period,omitempty" yaml:"lifetime_grace_period,omitempty"` -} - -// HMACAuthConsumerConfig is the rule config for hmac-auth plugin -// used in Consumer object. -// +k8s:deepcopy-gen=true -type HMACAuthConsumerConfig struct { - AccessKey string `json:"access_key" yaml:"access_key"` - SecretKey string `json:"secret_key" yaml:"secret_key"` - Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"` - ClockSkew int64 `json:"clock_skew,omitempty" yaml:"clock_skew,omitempty"` - SignedHeaders []string `json:"signed_headers,omitempty" yaml:"signed_headers,omitempty"` - KeepHeaders bool `json:"keep_headers,omitempty" yaml:"keep_headers,omitempty"` - EncodeURIParams bool `json:"encode_uri_params,omitempty" yaml:"encode_uri_params,omitempty"` - ValidateRequestBody bool `json:"validate_request_body,omitempty" yaml:"validate_request_body,omitempty"` - MaxReqBody int64 `json:"max_req_body,omitempty" yaml:"max_req_body,omitempty"` -} - -// LDAPAuthConsumerConfig is the rule config for ldap-auth plugin -// used in Consumer object. -// +k8s:deepcopy-gen=true -type LDAPAuthConsumerConfig struct { - UserDN string `json:"user_dn"` -} - -// BasicAuthRouteConfig is the rule config for basic-auth plugin -// used in Route object. -// +k8s:deepcopy-gen=true -type BasicAuthRouteConfig struct{} - -// WolfRBACConsumerConfig is the rule config for wolf-rbac plugin -// used in Consumer object. -// +k8s:deepcopy-gen=true -type WolfRBACConsumerConfig struct { - Server string `json:"server,omitempty"` - Appid string `json:"appid,omitempty"` - HeaderPrefix string `json:"header_prefix,omitempty"` -} - -// RewriteConfig is the rule config for proxy-rewrite plugin. -// +k8s:deepcopy-gen=true -type RewriteConfig struct { - RewriteTarget string `json:"uri,omitempty"` - RewriteTargetRegex []string `json:"regex_uri,omitempty"` - Headers *Headers `json:"headers,omitempty"` - Host string `json:"host,omitempty"` -} - -// ResponseRewriteConfig is the rule config for response-rewrite plugin. -// +k8s:deepcopy-gen=true -type ResponseRewriteConfig struct { - StatusCode int `json:"status_code,omitempty"` - Body string `json:"body,omitempty"` - BodyBase64 bool `json:"body_base64,omitempty"` - Headers *ResponseHeaders `json:"headers,omitempty"` - LuaRestyExpr []expr.Expr `json:"vars,omitempty"` - Filters []map[string]string `json:"filters,omitempty"` -} - -// RedirectConfig is the rule config for redirect plugin. -// +k8s:deepcopy-gen=true -type RedirectConfig struct { - HttpToHttps bool `json:"http_to_https,omitempty"` - URI string `json:"uri,omitempty"` - RetCode int `json:"ret_code,omitempty"` -} - -// ForwardAuthConfig is the rule config for forward-auth plugin. -// +k8s:deepcopy-gen=true -type ForwardAuthConfig struct { - URI string `json:"uri"` - SSLVerify bool `json:"ssl_verify"` - RequestHeaders []string `json:"request_headers,omitempty"` - UpstreamHeaders []string `json:"upstream_headers,omitempty"` - ClientHeaders []string `json:"client_headers,omitempty"` -} - -// BasicAuthConfig is the rule config for basic-auth plugin. -// +k8s:deepcopy-gen=true -type BasicAuthConfig struct { -} - -// KeyAuthConfig is the rule config for key-auth plugin. -// +k8s:deepcopy-gen=true -type KeyAuthConfig struct { -} - -// RequestMirror is the rule config for proxy-mirror plugin. -// +k8s:deepcopy-gen=true -type RequestMirror struct { - Host string `json:"host"` -} - -// +k8s:deepcopy-gen=true -type Headers struct { - Set map[string]string `json:"set" yaml:"set"` - Add map[string]string `json:"add" yaml:"add"` - Remove []string `json:"remove" yaml:"remove"` -} - -// +k8s:deepcopy-gen=true -type ResponseHeaders struct { - Set map[string]string `json:"set" yaml:"set"` - Add []string `json:"add" yaml:"add"` - Remove []string `json:"remove" yaml:"remove"` -} diff --git a/api/dashboard/v1/types.go b/api/dashboard/v1/types.go deleted file mode 100644 index fad8c700d..000000000 --- a/api/dashboard/v1/types.go +++ /dev/null @@ -1,947 +0,0 @@ -// 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 v1 - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "database/sql/driver" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - "time" - - clientv3 "go.etcd.io/etcd/client/v3" -) - -const ( - // HashOnVars means the hash scope is variable. - HashOnVars = "vars" - // HashOnVarsCombination means the hash scope is the - // variable combination. - HashOnVarsCombination = "vars_combinations" - // HashOnHeader means the hash scope is HTTP request - // headers. - HashOnHeader = "header" - // HashOnCookie means the hash scope is HTTP Cookie. - HashOnCookie = "cookie" - // HashOnConsumer means the hash scope is APISIX consumer. - HashOnConsumer = "consumer" - - // LbRoundRobin is the round robin load balancer. - LbRoundRobin = "roundrobin" - // LbConsistentHash is the consistent hash load balancer. - LbConsistentHash = "chash" - // LbEwma is the ewma load balancer. - LbEwma = "ewma" - // LbLeaseConn is the least connection load balancer. - LbLeastConn = "least_conn" - - // SchemeHTTP represents the HTTP protocol. - SchemeHTTP = "http" - // SchemeGRPC represents the GRPC protocol. - SchemeGRPC = "grpc" - // SchemeHTTPS represents the HTTPS protocol. - SchemeHTTPS = "https" - // SchemeGRPCS represents the GRPCS protocol. - SchemeGRPCS = "grpcs" - // SchemeTCP represents the TCP protocol. - SchemeTCP = "tcp" - // SchemeUDP represents the UDP protocol. - SchemeUDP = "udp" - - // HealthCheckHTTP represents the HTTP kind health check. - HealthCheckHTTP = "http" - // HealthCheckHTTPS represents the HTTPS kind health check. - HealthCheckHTTPS = "https" - // HealthCheckTCP represents the TCP kind health check. - HealthCheckTCP = "tcp" - - // HealthCheckMaxConsecutiveNumber is the max number for - // the consecutive success/failure in upstream health check. - HealthCheckMaxConsecutiveNumber = 254 - // ActiveHealthCheckMinInterval is the minimum interval for - // the active health check. - ActiveHealthCheckMinInterval = time.Second - - // DefaultUpstreamTimeout represents the default connect, - // read and send timeout (in seconds) with upstreams. - DefaultUpstreamTimeout = 60 - - // PassHostPass represents pass option for pass_host Upstream settings. - PassHostPass = "pass" - // PassHostPass represents node option for pass_host Upstream settings. - PassHostNode = "node" - // PassHostPass represents rewrite option for pass_host Upstream settings. - PassHostRewrite = "rewrite" -) - -var ValidSchemes map[string]struct{} = map[string]struct{}{ - SchemeHTTP: {}, - SchemeHTTPS: {}, - SchemeGRPC: {}, - SchemeGRPCS: {}, -} - -// Metadata contains all meta information about resources. -// +k8s:deepcopy-gen=true -type Metadata struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` -} - -func (m *Metadata) GetID() string { - return m.ID -} - -func (m *Metadata) GetName() string { - return m.Name -} - -func (m *Metadata) GetLabels() map[string]string { - return m.Labels -} - -// Upstream is the apisix upstream definition. -// +k8s:deepcopy-gen=true -type Upstream struct { - Metadata `json:",inline" yaml:",inline"` - - Type string `json:"type,omitempty" yaml:"type,omitempty"` - HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` - Checks *UpstreamHealthCheck `json:"checks,omitempty" yaml:"checks,omitempty"` - Nodes UpstreamNodes `json:"nodes" yaml:"nodes"` - Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` - Retries *int `json:"retries,omitempty" yaml:"retries,omitempty"` - Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` - TLS *ClientTLS `json:"tls,omitempty" yaml:"tls,omitempty"` - PassHost string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` - UpstreamHost string `json:"upstream_host,omitempty" yaml:"upstream_host,omitempty"` - - // for Service Discovery - ServiceName string `json:"service_name,omitempty" yaml:"service_name,omitempty"` - DiscoveryType string `json:"discovery_type,omitempty" yaml:"discovery_type,omitempty"` - DiscoveryArgs map[string]string `json:"discovery_args,omitempty" yaml:"discovery_args,omitempty"` -} - -type ServiceType string - -const ( - ServiceTypeHTTP ServiceType = "http" - ServiceTypeStream ServiceType = "stream" -) - -// Upstream is the apisix upstream definition. -// +k8s:deepcopy-gen=true -type Service struct { - Metadata `json:",inline" yaml:",inline"` - Type ServiceType `json:"type,omitempty" yaml:"type,omitempty"` - Upstream *Upstream `json:"upstream,omitempty" yaml:"upstream,omitempty"` - Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` -} - -// Route apisix route object -// +k8s:deepcopy-gen=true -type Route struct { - Metadata `json:",inline" yaml:",inline"` - Host string `json:"host,omitempty" yaml:"host,omitempty"` - Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"` - Uri string `json:"uri,omitempty" yaml:"uri,omitempty"` - Priority int `json:"priority,omitempty" yaml:"priority,omitempty"` - Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` - Vars Vars `json:"vars,omitempty" yaml:"vars,omitempty"` - Paths []string `json:"paths,omitempty" yaml:"paths,omitempty"` - Methods []string `json:"methods,omitempty" yaml:"methods,omitempty"` - EnableWebsocket bool `json:"enable_websocket,omitempty" yaml:"enable_websocket,omitempty"` - RemoteAddrs []string `json:"remote_addrs,omitempty" yaml:"remote_addrs,omitempty"` - ServiceID string `json:"service_id,omitempty" yaml:"service_id,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` - PluginConfigId string `json:"plugin_config_id,omitempty" yaml:"plugin_config_id,omitempty"` - FilterFunc string `json:"filter_func,omitempty" yaml:"filter_func,omitempty"` -} - -// Vars represents the route match expressions of APISIX. -type Vars [][]StringOrSlice - -// UnmarshalJSON implements json.Unmarshaler interface. -// lua-cjson doesn't distinguish empty array and table, -// and by default empty array will be encoded as '{}'. -// We have to maintain the compatibility. -func (vars *Vars) UnmarshalJSON(p []byte) error { - if p[0] == '{' { - if len(p) != 2 { - return errors.New("unexpected non-empty object") - } - return nil - } - var data [][]StringOrSlice - if err := json.Unmarshal(p, &data); err != nil { - return err - } - *vars = data - return nil -} - -// StringOrSlice represents a string or a string slice. -// TODO Do not use interface{} to avoid the reflection overheads. -// +k8s:deepcopy-gen=true -type StringOrSlice struct { - StrVal string `json:"-"` - SliceVal []string `json:"-"` -} - -func (s *StringOrSlice) MarshalJSON() ([]byte, error) { - var ( - p []byte - err error - ) - if s.SliceVal != nil { - p, err = json.Marshal(s.SliceVal) - } else { - p, err = json.Marshal(s.StrVal) - } - return p, err -} - -func (s *StringOrSlice) UnmarshalJSON(p []byte) error { - var err error - - if len(p) == 0 { - return errors.New("empty object") - } - if p[0] == '[' { - err = json.Unmarshal(p, &s.SliceVal) - } else { - err = json.Unmarshal(p, &s.StrVal) - } - return err -} - -type Plugins map[string]any - -func (p *Plugins) DeepCopyInto(out *Plugins) { - b, _ := json.Marshal(&p) - _ = json.Unmarshal(b, out) -} - -func (p Plugins) DeepCopy() Plugins { - if p == nil { - return nil - } - out := make(Plugins) - p.DeepCopyInto(&out) - return out -} - -// ClientTLS is tls cert and key use in mTLS -type ClientTLS struct { - Cert string `json:"client_cert,omitempty" yaml:"client_cert,omitempty"` - Key string `json:"client_key,omitempty" yaml:"client_key,omitempty"` -} - -// UpstreamTimeout represents the timeout settings on Upstream. -type UpstreamTimeout struct { - // Connect is the connect timeout - Connect int `json:"connect" yaml:"connect"` - // Send is the send timeout - Send int `json:"send" yaml:"send"` - // Read is the read timeout - Read int `json:"read" yaml:"read"` -} - -// UpstreamNodes is the upstream node list. -type UpstreamNodes []UpstreamNode - -// UnmarshalJSON implements json.Unmarshaler interface. -// lua-cjson doesn't distinguish empty array and table, -// and by default empty array will be encoded as '{}'. -// We have to maintain the compatibility. -func (n *UpstreamNodes) UnmarshalJSON(p []byte) error { - var data []UpstreamNode - if p[0] == '{' { - value := map[string]float64{} - if err := json.Unmarshal(p, &value); err != nil { - return err - } - for k, v := range value { - node, err := mapKV2Node(k, v) - if err != nil { - return err - } - data = append(data, *node) - } - *n = data - return nil - } - if err := json.Unmarshal(p, &data); err != nil { - return err - } - *n = data - return nil -} - -// MarshalJSON is used to implement custom json.MarshalJSON -func (up Upstream) MarshalJSON() ([]byte, error) { - - if up.DiscoveryType != "" { - return json.Marshal(&struct { - Metadata `json:",inline" yaml:",inline"` - - Type string `json:"type,omitempty" yaml:"type,omitempty"` - HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` - Checks *UpstreamHealthCheck `json:"checks,omitempty" yaml:"checks,omitempty"` - // Nodes UpstreamNodes `json:"nodes" yaml:"nodes"` - Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` - Retries *int `json:"retries,omitempty" yaml:"retries,omitempty"` - Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` - HostPass string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` - UpstreamHost string `json:"upstream_host,omitempty" yaml:"upstream_host,omitempty"` - TLS *ClientTLS `json:"tls,omitempty" yaml:"tls,omitempty"` - - // for Service Discovery - ServiceName string `json:"service_name,omitempty" yaml:"service_name,omitempty"` - DiscoveryType string `json:"discovery_type,omitempty" yaml:"discovery_type,omitempty"` - DiscoveryArgs map[string]string `json:"discovery_args,omitempty" yaml:"discovery_args,omitempty"` - }{ - Metadata: up.Metadata, - - Type: up.Type, - HashOn: up.HashOn, - Key: up.Key, - Checks: up.Checks, - // Nodes: up.Nodes, - Scheme: up.Scheme, - Retries: up.Retries, - Timeout: up.Timeout, - HostPass: up.PassHost, - UpstreamHost: up.UpstreamHost, - TLS: up.TLS, - - ServiceName: up.ServiceName, - DiscoveryType: up.DiscoveryType, - DiscoveryArgs: up.DiscoveryArgs, - }) - } else { - return json.Marshal(&struct { - Metadata `json:",inline" yaml:",inline"` - - Type string `json:"type,omitempty" yaml:"type,omitempty"` - HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` - Checks *UpstreamHealthCheck `json:"checks,omitempty" yaml:"checks,omitempty"` - Nodes UpstreamNodes `json:"nodes" yaml:"nodes"` - Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` - Retries *int `json:"retries,omitempty" yaml:"retries,omitempty"` - Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` - HostPass string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` - UpstreamHost string `json:"upstream_host,omitempty" yaml:"upstream_host,omitempty"` - TLS *ClientTLS `json:"tls,omitempty" yaml:"tls,omitempty"` - - // for Service Discovery - // ServiceName string `json:"service_name,omitempty" yaml:"service_name,omitempty"` - // DiscoveryType string `json:"discovery_type,omitempty" yaml:"discovery_type,omitempty"` - // DiscoveryArgs map[string]string `json:"discovery_args,omitempty" yaml:"discovery_args,omitempty"` - }{ - Metadata: up.Metadata, - - Type: up.Type, - HashOn: up.HashOn, - Key: up.Key, - Checks: up.Checks, - Nodes: up.Nodes, - Scheme: up.Scheme, - Retries: up.Retries, - Timeout: up.Timeout, - HostPass: up.PassHost, - UpstreamHost: up.UpstreamHost, - TLS: up.TLS, - - // ServiceName: up.ServiceName, - // DiscoveryType: up.DiscoveryType, - // DiscoveryArgs: up.DiscoveryArgs, - }) - } - -} - -func mapKV2Node(key string, val float64) (*UpstreamNode, error) { - hp := strings.Split(key, ":") - host := hp[0] - // according to APISIX upstream nodes policy, port is required - port := "80" - - if len(hp) > 2 { - return nil, errors.New("invalid upstream node") - } else if len(hp) == 2 { - port = hp[1] - } - - portInt, err := strconv.Atoi(port) - if err != nil { - return nil, fmt.Errorf("parse port to int fail: %s", err.Error()) - } - - node := &UpstreamNode{ - Host: host, - Port: portInt, - Weight: int(val), - } - - return node, nil -} - -// UpstreamNode is the node in upstream -// +k8s:deepcopy-gen=true -type UpstreamNode struct { - Host string `json:"host,omitempty" yaml:"host,omitempty"` - Port int `json:"port,omitempty" yaml:"port,omitempty"` - Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` -} - -// UpstreamHealthCheck defines the active and/or passive health check for an Upstream, -// with the upstream health check feature, pods can be kicked out or joined in quickly, -// if the feedback of Kubernetes liveness/readiness probe is long. -// +k8s:deepcopy-gen=true -type UpstreamHealthCheck struct { - Active *UpstreamActiveHealthCheck `json:"active" yaml:"active"` - Passive *UpstreamPassiveHealthCheck `json:"passive,omitempty" yaml:"passive,omitempty"` -} - -// UpstreamActiveHealthCheck defines the active kind of upstream health check. -// +k8s:deepcopy-gen=true -type UpstreamActiveHealthCheck struct { - Type string `json:"type,omitempty" yaml:"type,omitempty"` - Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` - Concurrency int `json:"concurrency,omitempty" yaml:"concurrency,omitempty"` - Host string `json:"host,omitempty" yaml:"host,omitempty"` - Port int32 `json:"port,omitempty" yaml:"port,omitempty"` - HTTPPath string `json:"http_path,omitempty" yaml:"http_path,omitempty"` - HTTPSVerifyCert bool `json:"https_verify_certificate,omitempty" yaml:"https_verify_certificate,omitempty"` - HTTPRequestHeaders []string `json:"req_headers,omitempty" yaml:"req_headers,omitempty"` - Healthy UpstreamActiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"` - Unhealthy UpstreamActiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"` -} - -// UpstreamPassiveHealthCheck defines the passive kind of upstream health check. -// +k8s:deepcopy-gen=true -type UpstreamPassiveHealthCheck struct { - Type string `json:"type,omitempty" yaml:"type,omitempty"` - Healthy UpstreamPassiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"` - Unhealthy UpstreamPassiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"` -} - -// UpstreamActiveHealthCheckHealthy defines the conditions to judge whether -// an upstream node is healthy with the active manner. -// +k8s:deepcopy-gen=true -type UpstreamActiveHealthCheckHealthy struct { - UpstreamPassiveHealthCheckHealthy `json:",inline" yaml:",inline"` - - Interval int `json:"interval,omitempty" yaml:"interval,omitempty"` -} - -// UpstreamPassiveHealthCheckHealthy defines the conditions to judge whether -// an upstream node is healthy with the passive manner. -// +k8s:deepcopy-gen=true -type UpstreamPassiveHealthCheckHealthy struct { - HTTPStatuses []int `json:"http_statuses,omitempty" yaml:"http_statuses,omitempty"` - Successes int `json:"successes,omitempty" yaml:"successes,omitempty"` -} - -// UpstreamActiveHealthCheckUnhealthy defines the conditions to judge whether -// an upstream node is unhealthy with the active manager. -// +k8s:deepcopy-gen=true -type UpstreamActiveHealthCheckUnhealthy struct { - UpstreamPassiveHealthCheckUnhealthy `json:",inline" yaml:",inline"` - - Interval int `json:"interval,omitempty" yaml:"interval,omitempty"` -} - -// UpstreamPassiveHealthCheckUnhealthy defines the conditions to judge whether -// an upstream node is unhealthy with the passive manager. -// +k8s:deepcopy-gen=true -type UpstreamPassiveHealthCheckUnhealthy struct { - HTTPStatuses []int `json:"http_statuses,omitempty" yaml:"http_statuses,omitempty"` - HTTPFailures int `json:"http_failures,omitempty" yaml:"http_failures,omitempty"` - TCPFailures int `json:"tcp_failures,omitempty" yaml:"tcp_failures,omitempty"` - Timeouts int `json:"timeouts,omitempty" yaml:"timeouts,omitempty"` -} - -// Ssl apisix ssl object -// +k8s:deepcopy-gen=true -type Ssl struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Snis []string `json:"snis,omitempty" yaml:"snis,omitempty"` - Cert string `json:"cert,omitempty" yaml:"cert,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` - Status int `json:"status,omitempty" yaml:"status,omitempty"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` - Client *MutualTLSClientConfig `json:"client,omitempty" yaml:"client,omitempty"` -} - -// MutualTLSClientConfig apisix SSL client field -// +k8s:deepcopy-gen=true -type MutualTLSClientConfig struct { - CA string `json:"ca,omitempty" yaml:"ca,omitempty"` - Depth int `json:"depth,omitempty" yaml:"depth,omitempty"` - SkipMTLSUriRegex []string `json:"skip_mtls_uri_regex,omitempty" yaml:"skip_mtls_uri_regex, omitempty"` -} - -// StreamRoute represents the stream_route object in APISIX. -// +k8s:deepcopy-gen=true -type StreamRoute struct { - // TODO metadata should use Metadata type - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` - ServerPort int32 `json:"server_port,omitempty" yaml:"server_port,omitempty"` - SNI string `json:"sni,omitempty" yaml:"sni,omitempty"` - ServiceID string `json:"service_id,omitempty" yaml:"service_id,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` -} - -// GlobalRule represents the global_rule object in APISIX. -// +k8s:deepcopy-gen=true -type GlobalRule struct { - ID string `json:"id" yaml:"id"` - Plugins Plugins `json:"plugins" yaml:"plugins"` -} - -// Consumer represents the consumer object in APISIX. -// +k8s:deepcopy-gen=true -type Consumer struct { - Username string `json:"username" yaml:"username"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` -} - -// PluginConfig apisix plugin object -// +k8s:deepcopy-gen=true -type PluginConfig struct { - Metadata `json:",inline" yaml:",inline"` - Plugins Plugins `json:"plugins" yaml:"plugins"` -} - -type PluginMetadata struct { - Name string - Metadata map[string]any -} - -// NewDefaultUpstream returns an empty Upstream with default values. -func NewDefaultService() *Service { - return &Service{ - Metadata: Metadata{ - Labels: map[string]string{ - "managed-by": "api7-ingress-controller", - }, - }, - Plugins: make(Plugins), - } -} - -// NewDefaultRoute returns an empty Route with default values. -func NewDefaultRoute() *Route { - return &Route{ - Metadata: Metadata{ - Desc: "Created by api7-ingress-controller, DO NOT modify it manually", - Labels: map[string]string{ - "managed-by": "api7-ingress-controller", - }, - }, - } -} - -func NewDefaultUpstream() *Upstream { - return &Upstream{ - Type: LbRoundRobin, - Key: "", - Nodes: make(UpstreamNodes, 0), - Scheme: SchemeHTTP, - Metadata: Metadata{ - Desc: "Created by apisix-ingress-controller, DO NOT modify it manually", - Labels: map[string]string{ - "managed-by": "apisix-ingress-controller", - }, - }, - } -} - -// NewDefaultStreamRoute returns an empty StreamRoute with default values. -func NewDefaultStreamRoute() *StreamRoute { - return &StreamRoute{ - Desc: "Created by api7-ingress-controller, DO NOT modify it manually", - Labels: map[string]string{ - "managed-by": "api7-ingress-controller", - }, - } -} - -// NewDefaultConsumer returns an empty Consumer with default values. -func NewDefaultConsumer() *Consumer { - return &Consumer{ - Desc: "Created by api7-ingress-controller, DO NOT modify it manually", - Labels: map[string]string{ - "managed-by": "api7-ingress-controller", - }, - } -} - -// NewDefaultPluginConfig returns an empty PluginConfig with default values. -func NewDefaultPluginConfig() *PluginConfig { - return &PluginConfig{ - Metadata: Metadata{ - Desc: "Created by api7-ingress-controller, DO NOT modify it manually", - Labels: map[string]string{ - "managed-by": "api7-ingress-controller", - }, - }, - Plugins: make(Plugins), - } -} - -// NewDefaultGlobalRule returns an empty PluginConfig with default values. -func NewDefaultGlobalRule() *GlobalRule { - return &GlobalRule{ - Plugins: make(Plugins), - } -} - -// ComposeUpstreamName uses namespace, name, subset (optional), port, resolveGranularity info to compose -// the upstream name. -// the resolveGranularity is not composited in the upstream name when it is endpoint. -func ComposeUpstreamName(namespace, name string, port int32) string { - pstr := strconv.Itoa(int(port)) - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - var p []byte - plen := len(namespace) + len(name) + len(pstr) + 2 - - p = make([]byte, 0, plen) - buf := bytes.NewBuffer(p) - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - buf.WriteByte('_') - buf.WriteString(pstr) - - return buf.String() -} - -func ComposeServiceNameWithRule(namespace, name string, rule string) string { - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - var p []byte - plen := len(namespace) + len(name) + 2 - - p = make([]byte, 0, plen) - buf := bytes.NewBuffer(p) - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - buf.WriteByte('_') - buf.WriteString(rule) - - return buf.String() -} - -func ComposeUpstreamNameWithRule(namespace, name string, rule string) string { - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - var p []byte - plen := len(namespace) + len(name) + 2 - - p = make([]byte, 0, plen) - buf := bytes.NewBuffer(p) - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - buf.WriteByte('_') - buf.WriteString(rule) - - return buf.String() -} - -// ComposeExternalUpstreamName uses ApisixUpstream namespace, name to compose the upstream name. -func ComposeExternalUpstreamName(namespace, name string) string { - return namespace + "_" + name -} - -// ComposeRouteName uses namespace, name and rule name to compose -// the route name. -func ComposeRouteName(namespace, name string, rule string) string { - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - p := make([]byte, 0, len(namespace)+len(name)+len(rule)+2) - buf := bytes.NewBuffer(p) - - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - buf.WriteByte('_') - buf.WriteString(rule) - - return buf.String() -} - -// ComposeStreamRouteName uses namespace, name and rule name to compose -// the stream_route name. -func ComposeStreamRouteName(namespace, name string, rule string) string { - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - p := make([]byte, 0, len(namespace)+len(name)+len(rule)+6) - buf := bytes.NewBuffer(p) - - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - buf.WriteByte('_') - buf.WriteString(rule) - buf.WriteString("_tcp") - - return buf.String() -} - -// ComposeConsumerName uses namespace and name of ApisixConsumer to compose -// the Consumer name. -func ComposeConsumerName(namespace, name string) string { - p := make([]byte, 0, len(namespace)+len(name)+1) - buf := bytes.NewBuffer(p) - - // TODO If APISIX modifies the consumer name schema, we can drop this. - buf.WriteString(strings.ReplaceAll(namespace, "-", "_")) - buf.WriteString("_") - buf.WriteString(strings.ReplaceAll(name, "-", "_")) - - return buf.String() -} - -// ComposePluginConfigName uses namespace, name to compose -// the plugin_config name. -func ComposePluginConfigName(namespace, name string) string { - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - p := make([]byte, 0, len(namespace)+len(name)+1) - buf := bytes.NewBuffer(p) - - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - - return buf.String() -} - -// ComposeGlobalRuleName uses namespace, name to compose -// the global_rule name. -func ComposeGlobalRuleName(namespace, name string) string { - // FIXME Use sync.Pool to reuse this buffer if the upstream - // name composing code path is hot. - p := make([]byte, 0, len(namespace)+len(name)+1) - buf := bytes.NewBuffer(p) - - buf.WriteString(namespace) - buf.WriteByte('_') - buf.WriteString(name) - - return buf.String() -} - -// Schema represents the schema of APISIX objects. -type Schema struct { - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Content string `json:"content,omitempty" yaml:"content,omitempty"` -} - -func (s *Schema) DeepCopyInto(out *Schema) { - b, _ := json.Marshal(&s) - _ = json.Unmarshal(b, out) -} - -func (s *Schema) DeepCopy() *Schema { - if s == nil { - return nil - } - out := new(Schema) - s.DeepCopyInto(out) - return out -} - -type GatewayGroup struct { - ID string `json:"id" gorm:"column:id; primaryKey; size:255;"` - ShortID string `json:"short_id" gorm:"column:short_id; size:255; uniqueIndex:UQE_gateway_group_short_id;"` - Name string `json:"name" gorm:"name; size:255;"` - OrgID string `json:"-" gorm:"org_id; size:255; index:gateway_group_org_id;"` - Type string `json:"type" gorm:"column:type; size:255; default:api7_gateway;"` - Description string `json:"description" gorm:"description; type:text;"` - Labels map[string]string `json:"labels,omitempty" gorm:"serializer:json; column:labels; type:text;"` - Config GatewayGroupBasicConfig `json:"config" gorm:"serializer:json; column:config; type:text;"` - RunningConfigID string `json:"-" gorm:"column:running_config_id; size:255;"` - ConfigVersion int64 `json:"-" gorm:"column:config_version;"` - AdminKeySalt string `json:"-" gorm:"column:admin_key_salt; size:255;"` - EncryptedAdminKey string `json:"-" gorm:"column:encrypted_admin_key; size:255;"` - EnforceServicePublishing bool `json:"enforce_service_publishing" gorm:"column:enforce_service_publishing; default:false;"` -} - -func (g *GatewayGroup) GetKeyPrefix() string { - return fmt.Sprintf("/gateway_groups/%s", g.ShortID) -} - -func (g *GatewayGroup) GetKeyPrefixEnd() string { - return clientv3.GetPrefixRangeEnd(g.GetKeyPrefix()) -} - -type GatewayGroupBasicConfig struct { - ImageTag string `json:"image_tag,omitempty"` -} - -func (GatewayGroup) TableName() string { - return "gateway_group" -} - -type GatewayGroupAdminKey struct { - Key string `json:"key" mask:"fixed"` -} - -type CertificateType string - -const ( - CertificateTypeEndpoint CertificateType = "Endpoint" - CertificateTypeIntermediate CertificateType = "Intermediate" - CertificateTypeRoot CertificateType = "Root" -) - -type AesEncrypt string - -var AESKeyring = "b2zanhtrq35f6j3m" - -func PKCS7Unpadding(plantText []byte) []byte { - length := len(plantText) - if length == 0 { - return plantText - } - padding := int(plantText[length-1]) - return plantText[:(length - padding)] -} - -func FAesDecrypt(encrypted string, keyring string) (string, error) { - if len(encrypted) == 0 { - return "", nil - } - ciphertext, err := base64.StdEncoding.DecodeString(encrypted) - if err != nil { - return "", err - } - - block, err := aes.NewCipher([]byte(keyring)) - if err != nil { - return "", err - } - if len(ciphertext)%aes.BlockSize != 0 { - return "", errors.New("block size cant be zero") - } - iv := []byte(keyring)[:aes.BlockSize] - mode := cipher.NewCBCDecrypter(block, iv) - mode.CryptBlocks(ciphertext, ciphertext) - - return string(PKCS7Unpadding(ciphertext)), nil -} - -func (aesEncrypt AesEncrypt) Value() (driver.Value, error) { - return FAesDecrypt(string(aesEncrypt), AESKeyring) -} - -func (aesEncrypt *AesEncrypt) Scan(value any) error { - var str string - switch v := value.(type) { - case string: // for postgres - str = v - case []uint8: // for mysql - str = string(v) - default: - return fmt.Errorf("invalid type scan from database driver: %T", value) - } - res, err := FAesDecrypt(str, AESKeyring) - if err == nil { - *aesEncrypt = AesEncrypt(res) - } - return err -} - -type BaseCertificate struct { - ID string `json:"id" gorm:"primaryKey; column:id; size:255;"` - Certificate string `json:"certificate" gorm:"column:certificate; type:text;"` - PrivateKey AesEncrypt `json:"private_key" gorm:"column:private_key; type:text;" mask:"fixed"` - Expiry Time `json:"expiry" gorm:"column:expiry"` - CreatedAt Time `json:"-" gorm:"column:created_at;autoCreateTime; <-:create;"` - UpdatedAt Time `json:"-" gorm:"column:updated_at;autoUpdateTime"` - Type CertificateType `json:"-" gorm:"column:type;"` -} - -type DataplaneCertificate struct { - *BaseCertificate - - GatewayGroupID string `json:"gateway_group_id" gorm:"column:gateway_group_id;size:255;"` - CACertificate string `json:"ca_certificate" gorm:"column:ca_certificate;type:text;"` -} - -func (DataplaneCertificate) TableName() string { - return "dataplane_certificate" -} - -type Time time.Time - -func (t *Time) UnmarshalJSON(data []byte) error { - ts, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return err - } - - *t = Time(time.Unix(ts, 0)) - return nil -} - -func (t Time) MarshalJSON() ([]byte, error) { - ts := (time.Time)(t).Unix() - return []byte(strconv.FormatInt(ts, 10)), nil -} - -func (t Time) String() string { - return strconv.FormatInt(time.Time(t).Unix(), 10) -} - -func (t *Time) Unix() int64 { - return time.Time(*t).Unix() -} - -func (t *Time) Scan(src any) error { - switch s := src.(type) { - case time.Time: - *t = Time(s) - default: - return fmt.Errorf("invalid time type from database driver: %T", src) - } - return nil -} - -func (t Time) Value() (driver.Value, error) { - return time.Time(t), nil -} diff --git a/api/dashboard/v1/zz_generated.deepcopy.go b/api/dashboard/v1/zz_generated.deepcopy.go deleted file mode 100644 index 241088840..000000000 --- a/api/dashboard/v1/zz_generated.deepcopy.go +++ /dev/null @@ -1,929 +0,0 @@ -//go:build !ignore_autogenerated - -/* -Copyright 2024. - -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. -*/ - -// Code generated by controller-gen. DO NOT EDIT. - -package v1 - -import ( - "github.com/incubator4/go-resty-expr/expr" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConfig. -func (in *BasicAuthConfig) DeepCopy() *BasicAuthConfig { - if in == nil { - return nil - } - out := new(BasicAuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BasicAuthConsumerConfig) DeepCopyInto(out *BasicAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConsumerConfig. -func (in *BasicAuthConsumerConfig) DeepCopy() *BasicAuthConsumerConfig { - if in == nil { - return nil - } - out := new(BasicAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BasicAuthRouteConfig) DeepCopyInto(out *BasicAuthRouteConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthRouteConfig. -func (in *BasicAuthRouteConfig) DeepCopy() *BasicAuthRouteConfig { - if in == nil { - return nil - } - out := new(BasicAuthRouteConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CSRFConfig) DeepCopyInto(out *CSRFConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSRFConfig. -func (in *CSRFConfig) DeepCopy() *CSRFConfig { - if in == nil { - return nil - } - out := new(CSRFConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Consumer) DeepCopyInto(out *Consumer) { - *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - out.Plugins = in.Plugins.DeepCopy() -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Consumer. -func (in *Consumer) DeepCopy() *Consumer { - if in == nil { - return nil - } - out := new(Consumer) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CorsConfig) DeepCopyInto(out *CorsConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsConfig. -func (in *CorsConfig) DeepCopy() *CorsConfig { - if in == nil { - return nil - } - out := new(CorsConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ForwardAuthConfig) DeepCopyInto(out *ForwardAuthConfig) { - *out = *in - if in.RequestHeaders != nil { - in, out := &in.RequestHeaders, &out.RequestHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.UpstreamHeaders != nil { - in, out := &in.UpstreamHeaders, &out.UpstreamHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ClientHeaders != nil { - in, out := &in.ClientHeaders, &out.ClientHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardAuthConfig. -func (in *ForwardAuthConfig) DeepCopy() *ForwardAuthConfig { - if in == nil { - return nil - } - out := new(ForwardAuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GlobalRule) DeepCopyInto(out *GlobalRule) { - *out = *in - out.Plugins = in.Plugins.DeepCopy() -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRule. -func (in *GlobalRule) DeepCopy() *GlobalRule { - if in == nil { - return nil - } - out := new(GlobalRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HMACAuthConsumerConfig) DeepCopyInto(out *HMACAuthConsumerConfig) { - *out = *in - if in.SignedHeaders != nil { - in, out := &in.SignedHeaders, &out.SignedHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HMACAuthConsumerConfig. -func (in *HMACAuthConsumerConfig) DeepCopy() *HMACAuthConsumerConfig { - if in == nil { - return nil - } - out := new(HMACAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Headers) DeepCopyInto(out *Headers) { - *out = *in - if in.Set != nil { - in, out := &in.Set, &out.Set - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Add != nil { - in, out := &in.Add, &out.Add - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Remove != nil { - in, out := &in.Remove, &out.Remove - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Headers. -func (in *Headers) DeepCopy() *Headers { - if in == nil { - return nil - } - out := new(Headers) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IPRestrictConfig) DeepCopyInto(out *IPRestrictConfig) { - *out = *in - if in.Allowlist != nil { - in, out := &in.Allowlist, &out.Allowlist - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Blocklist != nil { - in, out := &in.Blocklist, &out.Blocklist - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPRestrictConfig. -func (in *IPRestrictConfig) DeepCopy() *IPRestrictConfig { - if in == nil { - return nil - } - out := new(IPRestrictConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JwtAuthConsumerConfig) DeepCopyInto(out *JwtAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtAuthConsumerConfig. -func (in *JwtAuthConsumerConfig) DeepCopy() *JwtAuthConsumerConfig { - if in == nil { - return nil - } - out := new(JwtAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KeyAuthConfig) DeepCopyInto(out *KeyAuthConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConfig. -func (in *KeyAuthConfig) DeepCopy() *KeyAuthConfig { - if in == nil { - return nil - } - out := new(KeyAuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KeyAuthConsumerConfig) DeepCopyInto(out *KeyAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConsumerConfig. -func (in *KeyAuthConsumerConfig) DeepCopy() *KeyAuthConsumerConfig { - if in == nil { - return nil - } - out := new(KeyAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LDAPAuthConsumerConfig) DeepCopyInto(out *LDAPAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPAuthConsumerConfig. -func (in *LDAPAuthConsumerConfig) DeepCopy() *LDAPAuthConsumerConfig { - if in == nil { - return nil - } - out := new(LDAPAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Metadata) DeepCopyInto(out *Metadata) { - *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metadata. -func (in *Metadata) DeepCopy() *Metadata { - if in == nil { - return nil - } - out := new(Metadata) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MutualTLSClientConfig) DeepCopyInto(out *MutualTLSClientConfig) { - *out = *in - if in.SkipMTLSUriRegex != nil { - in, out := &in.SkipMTLSUriRegex, &out.SkipMTLSUriRegex - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MutualTLSClientConfig. -func (in *MutualTLSClientConfig) DeepCopy() *MutualTLSClientConfig { - if in == nil { - return nil - } - out := new(MutualTLSClientConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PluginConfig) DeepCopyInto(out *PluginConfig) { - *out = *in - in.Metadata.DeepCopyInto(&out.Metadata) - out.Plugins = in.Plugins.DeepCopy() -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginConfig. -func (in *PluginConfig) DeepCopy() *PluginConfig { - if in == nil { - return nil - } - out := new(PluginConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RedirectConfig) DeepCopyInto(out *RedirectConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectConfig. -func (in *RedirectConfig) DeepCopy() *RedirectConfig { - if in == nil { - return nil - } - out := new(RedirectConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RequestMirror) DeepCopyInto(out *RequestMirror) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestMirror. -func (in *RequestMirror) DeepCopy() *RequestMirror { - if in == nil { - return nil - } - out := new(RequestMirror) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResponseHeaders) DeepCopyInto(out *ResponseHeaders) { - *out = *in - if in.Set != nil { - in, out := &in.Set, &out.Set - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Add != nil { - in, out := &in.Add, &out.Add - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Remove != nil { - in, out := &in.Remove, &out.Remove - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseHeaders. -func (in *ResponseHeaders) DeepCopy() *ResponseHeaders { - if in == nil { - return nil - } - out := new(ResponseHeaders) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResponseRewriteConfig) DeepCopyInto(out *ResponseRewriteConfig) { - *out = *in - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = new(ResponseHeaders) - (*in).DeepCopyInto(*out) - } - if in.LuaRestyExpr != nil { - in, out := &in.LuaRestyExpr, &out.LuaRestyExpr - *out = make([]expr.Expr, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Filters != nil { - in, out := &in.Filters, &out.Filters - *out = make([]map[string]string, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseRewriteConfig. -func (in *ResponseRewriteConfig) DeepCopy() *ResponseRewriteConfig { - if in == nil { - return nil - } - out := new(ResponseRewriteConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RewriteConfig) DeepCopyInto(out *RewriteConfig) { - *out = *in - if in.RewriteTargetRegex != nil { - in, out := &in.RewriteTargetRegex, &out.RewriteTargetRegex - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = new(Headers) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RewriteConfig. -func (in *RewriteConfig) DeepCopy() *RewriteConfig { - if in == nil { - return nil - } - out := new(RewriteConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Route) DeepCopyInto(out *Route) { - *out = *in - in.Metadata.DeepCopyInto(&out.Metadata) - if in.Hosts != nil { - in, out := &in.Hosts, &out.Hosts - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Timeout != nil { - in, out := &in.Timeout, &out.Timeout - *out = new(UpstreamTimeout) - **out = **in - } - if in.Vars != nil { - in, out := &in.Vars, &out.Vars - *out = make(Vars, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = make([]StringOrSlice, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - } - } - if in.Paths != nil { - in, out := &in.Paths, &out.Paths - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.RemoteAddrs != nil { - in, out := &in.RemoteAddrs, &out.RemoteAddrs - *out = make([]string, len(*in)) - copy(*out, *in) - } - out.Plugins = in.Plugins.DeepCopy() -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route. -func (in *Route) DeepCopy() *Route { - if in == nil { - return nil - } - out := new(Route) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Service) DeepCopyInto(out *Service) { - *out = *in - in.Metadata.DeepCopyInto(&out.Metadata) - if in.Upstream != nil { - in, out := &in.Upstream, &out.Upstream - *out = new(Upstream) - (*in).DeepCopyInto(*out) - } - if in.Hosts != nil { - in, out := &in.Hosts, &out.Hosts - *out = make([]string, len(*in)) - copy(*out, *in) - } - out.Plugins = in.Plugins.DeepCopy() -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Service. -func (in *Service) DeepCopy() *Service { - if in == nil { - return nil - } - out := new(Service) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Ssl) DeepCopyInto(out *Ssl) { - *out = *in - if in.Snis != nil { - in, out := &in.Snis, &out.Snis - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Client != nil { - in, out := &in.Client, &out.Client - *out = new(MutualTLSClientConfig) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ssl. -func (in *Ssl) DeepCopy() *Ssl { - if in == nil { - return nil - } - out := new(Ssl) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StreamRoute) DeepCopyInto(out *StreamRoute) { - *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - out.Plugins = in.Plugins.DeepCopy() -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StreamRoute. -func (in *StreamRoute) DeepCopy() *StreamRoute { - if in == nil { - return nil - } - out := new(StreamRoute) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StringOrSlice) DeepCopyInto(out *StringOrSlice) { - *out = *in - if in.SliceVal != nil { - in, out := &in.SliceVal, &out.SliceVal - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringOrSlice. -func (in *StringOrSlice) DeepCopy() *StringOrSlice { - if in == nil { - return nil - } - out := new(StringOrSlice) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitConfig) DeepCopyInto(out *TrafficSplitConfig) { - *out = *in - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]TrafficSplitConfigRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfig. -func (in *TrafficSplitConfig) DeepCopy() *TrafficSplitConfig { - if in == nil { - return nil - } - out := new(TrafficSplitConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitConfigRule) DeepCopyInto(out *TrafficSplitConfigRule) { - *out = *in - if in.WeightedUpstreams != nil { - in, out := &in.WeightedUpstreams, &out.WeightedUpstreams - *out = make([]TrafficSplitConfigRuleWeightedUpstream, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfigRule. -func (in *TrafficSplitConfigRule) DeepCopy() *TrafficSplitConfigRule { - if in == nil { - return nil - } - out := new(TrafficSplitConfigRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitConfigRuleWeightedUpstream) DeepCopyInto(out *TrafficSplitConfigRuleWeightedUpstream) { - *out = *in - if in.Upstream != nil { - in, out := &in.Upstream, &out.Upstream - *out = new(Upstream) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfigRuleWeightedUpstream. -func (in *TrafficSplitConfigRuleWeightedUpstream) DeepCopy() *TrafficSplitConfigRuleWeightedUpstream { - if in == nil { - return nil - } - out := new(TrafficSplitConfigRuleWeightedUpstream) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Upstream) DeepCopyInto(out *Upstream) { - *out = *in - in.Metadata.DeepCopyInto(&out.Metadata) - if in.Checks != nil { - in, out := &in.Checks, &out.Checks - *out = new(UpstreamHealthCheck) - (*in).DeepCopyInto(*out) - } - if in.Nodes != nil { - in, out := &in.Nodes, &out.Nodes - *out = make(UpstreamNodes, len(*in)) - copy(*out, *in) - } - if in.Retries != nil { - in, out := &in.Retries, &out.Retries - *out = new(int) - **out = **in - } - if in.Timeout != nil { - in, out := &in.Timeout, &out.Timeout - *out = new(UpstreamTimeout) - **out = **in - } - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(ClientTLS) - **out = **in - } - if in.DiscoveryArgs != nil { - in, out := &in.DiscoveryArgs, &out.DiscoveryArgs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Upstream. -func (in *Upstream) DeepCopy() *Upstream { - if in == nil { - return nil - } - out := new(Upstream) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamActiveHealthCheck) DeepCopyInto(out *UpstreamActiveHealthCheck) { - *out = *in - if in.HTTPRequestHeaders != nil { - in, out := &in.HTTPRequestHeaders, &out.HTTPRequestHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.Healthy.DeepCopyInto(&out.Healthy) - in.Unhealthy.DeepCopyInto(&out.Unhealthy) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamActiveHealthCheck. -func (in *UpstreamActiveHealthCheck) DeepCopy() *UpstreamActiveHealthCheck { - if in == nil { - return nil - } - out := new(UpstreamActiveHealthCheck) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamActiveHealthCheckHealthy) DeepCopyInto(out *UpstreamActiveHealthCheckHealthy) { - *out = *in - in.UpstreamPassiveHealthCheckHealthy.DeepCopyInto(&out.UpstreamPassiveHealthCheckHealthy) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamActiveHealthCheckHealthy. -func (in *UpstreamActiveHealthCheckHealthy) DeepCopy() *UpstreamActiveHealthCheckHealthy { - if in == nil { - return nil - } - out := new(UpstreamActiveHealthCheckHealthy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamActiveHealthCheckUnhealthy) DeepCopyInto(out *UpstreamActiveHealthCheckUnhealthy) { - *out = *in - in.UpstreamPassiveHealthCheckUnhealthy.DeepCopyInto(&out.UpstreamPassiveHealthCheckUnhealthy) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamActiveHealthCheckUnhealthy. -func (in *UpstreamActiveHealthCheckUnhealthy) DeepCopy() *UpstreamActiveHealthCheckUnhealthy { - if in == nil { - return nil - } - out := new(UpstreamActiveHealthCheckUnhealthy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamHealthCheck) DeepCopyInto(out *UpstreamHealthCheck) { - *out = *in - if in.Active != nil { - in, out := &in.Active, &out.Active - *out = new(UpstreamActiveHealthCheck) - (*in).DeepCopyInto(*out) - } - if in.Passive != nil { - in, out := &in.Passive, &out.Passive - *out = new(UpstreamPassiveHealthCheck) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamHealthCheck. -func (in *UpstreamHealthCheck) DeepCopy() *UpstreamHealthCheck { - if in == nil { - return nil - } - out := new(UpstreamHealthCheck) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamNode) DeepCopyInto(out *UpstreamNode) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamNode. -func (in *UpstreamNode) DeepCopy() *UpstreamNode { - if in == nil { - return nil - } - out := new(UpstreamNode) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamPassiveHealthCheck) DeepCopyInto(out *UpstreamPassiveHealthCheck) { - *out = *in - in.Healthy.DeepCopyInto(&out.Healthy) - in.Unhealthy.DeepCopyInto(&out.Unhealthy) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamPassiveHealthCheck. -func (in *UpstreamPassiveHealthCheck) DeepCopy() *UpstreamPassiveHealthCheck { - if in == nil { - return nil - } - out := new(UpstreamPassiveHealthCheck) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamPassiveHealthCheckHealthy) DeepCopyInto(out *UpstreamPassiveHealthCheckHealthy) { - *out = *in - if in.HTTPStatuses != nil { - in, out := &in.HTTPStatuses, &out.HTTPStatuses - *out = make([]int, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamPassiveHealthCheckHealthy. -func (in *UpstreamPassiveHealthCheckHealthy) DeepCopy() *UpstreamPassiveHealthCheckHealthy { - if in == nil { - return nil - } - out := new(UpstreamPassiveHealthCheckHealthy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UpstreamPassiveHealthCheckUnhealthy) DeepCopyInto(out *UpstreamPassiveHealthCheckUnhealthy) { - *out = *in - if in.HTTPStatuses != nil { - in, out := &in.HTTPStatuses, &out.HTTPStatuses - *out = make([]int, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamPassiveHealthCheckUnhealthy. -func (in *UpstreamPassiveHealthCheckUnhealthy) DeepCopy() *UpstreamPassiveHealthCheckUnhealthy { - if in == nil { - return nil - } - out := new(UpstreamPassiveHealthCheckUnhealthy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WolfRBACConsumerConfig) DeepCopyInto(out *WolfRBACConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WolfRBACConsumerConfig. -func (in *WolfRBACConsumerConfig) DeepCopy() *WolfRBACConsumerConfig { - if in == nil { - return nil - } - out := new(WolfRBACConsumerConfig) - in.DeepCopyInto(out) - return out -} diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index b7ebc5fa9..90bc88600 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -22,8 +22,8 @@ type BackendTrafficPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // BackendTrafficPolicySpec defines traffic handling policies applied to backend services, - // such as load balancing strategy, connection settings, and failover behavior. + // BackendTrafficPolicySpec defines traffic handling policies applied to backend services, + // such as load balancing strategy, connection settings, and failover behavior. Spec BackendTrafficPolicySpec `json:"spec,omitempty"` Status PolicyStatus `json:"status,omitempty"` } diff --git a/api/v1alpha1/consumer_types.go b/api/v1alpha1/consumer_types.go index 108c8f99a..7e75f3594 100644 --- a/api/v1alpha1/consumer_types.go +++ b/api/v1alpha1/consumer_types.go @@ -23,7 +23,7 @@ type Consumer struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // ConsumerSpec defines the configuration for a consumer, including consumer name, + // ConsumerSpec defines the configuration for a consumer, including consumer name, // authentication credentials, and plugin settings. Spec ConsumerSpec `json:"spec,omitempty"` Status Status `json:"status,omitempty"` @@ -31,11 +31,11 @@ type Consumer struct { type ConsumerSpec struct { // GatewayRef specifies the gateway details. - GatewayRef GatewayRef `json:"gatewayRef,omitempty"` + GatewayRef GatewayRef `json:"gatewayRef,omitempty"` // Credentials specifies the credential details of a consumer. Credentials []Credential `json:"credentials,omitempty"` // Plugins define the plugins associated with a consumer. - Plugins []Plugin `json:"plugins,omitempty"` + Plugins []Plugin `json:"plugins,omitempty"` } type GatewayRef struct { @@ -48,7 +48,7 @@ type GatewayRef struct { Kind *string `json:"kind,omitempty"` // Group is the API group the resource belongs to. Default is `gateway.networking.k8s.io`. // +kubebuilder:default=gateway.networking.k8s.io - Group *string `json:"group,omitempty"` + Group *string `json:"group,omitempty"` // Namespace is namespace of the resource. Namespace *string `json:"namespace,omitempty"` } @@ -58,18 +58,18 @@ type Credential struct { // +kubebuilder:validation:Enum=jwt-auth;basic-auth;key-auth;hmac-auth; // Type specifies the type of authentication to configure credentials for. // Can be one of `jwt-auth`, `basic-auth`, `key-auth`, or `hmac-auth`. - Type string `json:"type"` + Type string `json:"type"` // Config specifies the credential details for authentication. - Config apiextensionsv1.JSON `json:"config,omitempty"` + Config apiextensionsv1.JSON `json:"config,omitempty"` // SecretRef references to the Secret that contains the credentials. - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *SecretReference `json:"secretRef,omitempty"` // Name is the name of the credential. - Name string `json:"name,omitempty"` + Name string `json:"name,omitempty"` } type SecretReference struct { // Name is the name of the secret. - Name string `json:"name"` + Name string `json:"name"` // Namespace is the namespace of the secret. Namespace *string `json:"namespace,omitempty"` } diff --git a/api/v1alpha1/gatewayproxy_types.go b/api/v1alpha1/gatewayproxy_types.go index cafd596fe..871d43bda 100644 --- a/api/v1alpha1/gatewayproxy_types.go +++ b/api/v1alpha1/gatewayproxy_types.go @@ -27,14 +27,14 @@ type GatewayProxySpec struct { // PublishService specifies the LoadBalancer-type Service whose external address the controller uses to // update the status of Ingress resources. - PublishService string `json:"publishService,omitempty"` + PublishService string `json:"publishService,omitempty"` // StatusAddress specifies the external IP addresses that the controller uses to populate the status field // of GatewayProxy or Ingress resources for developers to access. - StatusAddress []string `json:"statusAddress,omitempty"` + StatusAddress []string `json:"statusAddress,omitempty"` // Provider configures the provider details. - Provider *GatewayProxyProvider `json:"provider,omitempty"` + Provider *GatewayProxyProvider `json:"provider,omitempty"` // Plugins configure global plugins. - Plugins []GatewayProxyPlugin `json:"plugins,omitempty"` + Plugins []GatewayProxyPlugin `json:"plugins,omitempty"` // PluginMetadata configures common configurations shared by all plugin instances of the same name. PluginMetadata map[string]apiextensionsv1.JSON `json:"pluginMetadata,omitempty"` } @@ -132,8 +132,8 @@ type GatewayProxy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // GatewayProxySpec defines the desired state and configuration of a GatewayProxy, - // including networking settings, global plugins, and plugin metadata. + // GatewayProxySpec defines the desired state and configuration of a GatewayProxy, + // including networking settings, global plugins, and plugin metadata. Spec GatewayProxySpec `json:"spec,omitempty"` } @@ -148,11 +148,11 @@ type GatewayProxyList struct { // GatewayProxyPlugin contains plugin configurations. type GatewayProxyPlugin struct { // Name is the name of the plugin. - Name string `json:"name,omitempty"` - // Enabled defines whether the plugin is enabled. - Enabled bool `json:"enabled,omitempty"` + Name string `json:"name,omitempty"` + // Enabled defines whether the plugin is enabled. + Enabled bool `json:"enabled,omitempty"` // Config defines the plugin's configuration details. - Config apiextensionsv1.JSON `json:"config,omitempty"` + Config apiextensionsv1.JSON `json:"config,omitempty"` } func init() { diff --git a/api/v1alpha1/httproutepolicy_types.go b/api/v1alpha1/httproutepolicy_types.go index 5cce9a362..82c37bddf 100644 --- a/api/v1alpha1/httproutepolicy_types.go +++ b/api/v1alpha1/httproutepolicy_types.go @@ -25,9 +25,9 @@ type HTTPRoutePolicySpec struct { // +kubebuilder:validation:MaxItems=16 TargetRefs []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName `json:"targetRefs"` // Priority sets the priority for route. A higher value sets a higher priority in route matching. - Priority *int64 `json:"priority,omitempty" yaml:"priority,omitempty"` + Priority *int64 `json:"priority,omitempty" yaml:"priority,omitempty"` // Vars sets the request matching conditions. - Vars []apiextensionsv1.JSON `json:"vars,omitempty" yaml:"vars,omitempty"` + Vars []apiextensionsv1.JSON `json:"vars,omitempty" yaml:"vars,omitempty"` } // +kubebuilder:object:root=true diff --git a/charts/values.yaml b/charts/values.yaml index 858eee5bc..1ad0833f2 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -33,7 +33,7 @@ deployment: - ALL topologySpreadConstraints: [] image: - repository: api7/api7-ingress-controller + repository: apache/apisix-ingress-controller pullPolicy: IfNotPresent tag: "dev" diff --git a/cmd/root/root.go b/cmd/root/root.go index 7d6ade457..e8b6fc718 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -110,7 +110,6 @@ func newAPISIXIngressController() *cobra.Command { return err } - // dashboard sdk log l, err := log.NewLogger( log.WithOutputFile("stderr"), log.WithLogLevel(cfg.LogLevel), diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index a86c091ec..fcc48cc92 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: api7/api7-ingress-controller + newName: apache/apisix-ingress-controller newTag: dev diff --git a/docs/concepts.md b/docs/concepts.md index c320753a6..91b92b61d 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -1,6 +1,6 @@ # Concepts -The APISIX Ingress Controller is used to manage the APISIX Gateway as either a standalone application or a Kubernetes-based application. It dynamically configures and manages the API7 Gateway using Gateway API resources. +The APISIX Ingress Controller is used to manage the APISIX Gateway as either a standalone application or a Kubernetes-based application. It dynamically configures and manages the APISIX Gateway using Gateway API resources. ## Architecture diff --git a/docs/gateway-api.md b/docs/gateway-api.md index 459ff5a83..70180c92a 100644 --- a/docs/gateway-api.md +++ b/docs/gateway-api.md @@ -29,7 +29,7 @@ For more information about Gateway API, please refer to [Gateway API](https://ga ## HTTPRoute -The HTTPRoute resource allows users to configure HTTP routing by matching HTTP traffic and forwarding it to Kubernetes backends. Currently, the only backend supported by API7 Gateway is the Service resource. +The HTTPRoute resource allows users to configure HTTP routing by matching HTTP traffic and forwarding it to Kubernetes backends. Currently, the only backend supported by APISIX Gateway is the Service resource. ### Example diff --git a/docs/quickstart.md b/docs/quickstart.md deleted file mode 100644 index da0a3a686..000000000 --- a/docs/quickstart.md +++ /dev/null @@ -1,60 +0,0 @@ -# Quickstart - -This quickstart guide will help you get started with APISIX Ingress Controller in a few simple steps. - -## Prerequisites - -* Kubernetes -* API7 Dashboard -* API7 Gateway - -Please ensure you have deployed the API7 Dashboard control plane. - -Note: Refer to the [Gateway API Release Changelog](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.0.0), it is recommended to use Kubernetes version 1.25+. - -## Installation - -Install the Gateway API CRDs: - -```shell -kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml - -``` - -Install The APISIX Ingress Controller: - -```shell -kubectl apply -f https://github.com/apache/apisix-ingress-controller/releases/download/install.yaml - -``` - -## Test HTTP Routing - -Install the GatewayClass, Gateway, HTTPRoute and httpbin example app: - -```shell -kubectl apply -f https://github.com/apache/apisix-ingress-controller/blob/release-v2-dev/examples/quickstart.yaml -``` - -Requests will be forwarded by the gateway to the httpbin application: - -```shell -curl http://{apisix_gateway_loadbalancer_ip}/headers -``` - -:::Note If the APISIX Gateway service without loadbalancer - -You can forward the local port to the APISIX Gateway service with the following command: - -```shell -# Listen on port 9080 locally, forwarding to 80 in the pod -kubectl port-forward svc/${apisix-gateway-svc} 9080:80 -n ${apisix_gateway_namespace} -``` - -Now you can send HTTP requests to access it: - -```shell -curl http://localhost:9080/headers -``` - -::: diff --git a/go.mod b/go.mod index 7dc11b838..dceee95cc 100644 --- a/go.mod +++ b/go.mod @@ -5,16 +5,10 @@ go 1.22.0 toolchain go1.22.5 require ( - github.com/Masterminds/sprig/v3 v3.2.3 github.com/api7/gopkg v0.2.1-0.20230601092738-0f3730f9b57a - github.com/gavv/httpexpect v2.0.0+incompatible - github.com/gavv/httpexpect/v2 v2.16.0 github.com/go-logr/logr v1.4.2 github.com/go-logr/zapr v1.3.0 - github.com/google/uuid v1.6.0 - github.com/gruntwork-io/terratest v0.47.0 github.com/hashicorp/go-memdb v1.3.4 - github.com/hashicorp/go-multierror v1.1.1 github.com/incubator4/go-resty-expr v0.1.1 github.com/onsi/ginkgo/v2 v2.20.0 github.com/onsi/gomega v1.34.1 @@ -22,15 +16,9 @@ require ( github.com/samber/lo v1.47.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 - github.com/xeipuuv/gojsonschema v1.2.0 - go.etcd.io/etcd/client/v3 v3.5.15 - go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/net v0.28.0 gopkg.in/yaml.v2 v2.4.0 - gorm.io/gorm v1.25.11 - helm.sh/helm/v3 v3.15.4 k8s.io/api v0.31.1 k8s.io/apiextensions-apiserver v0.31.1 k8s.io/apimachinery v0.31.1 @@ -38,151 +26,56 @@ require ( k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/controller-runtime v0.19.0 sigs.k8s.io/gateway-api v1.2.0 - sigs.k8s.io/yaml v1.4.0 ) require ( - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect - github.com/MakeNowJust/heredoc v1.0.0 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/Masterminds/squirrel v1.5.4 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect - github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect - github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.4 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.44.245 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/containerd/containerd v1.7.15 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-semver v0.3.1 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/docker/cli v25.0.1+incompatible // indirect - github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v26.1.4+incompatible // indirect - github.com/docker/docker-credential-helpers v0.7.0 // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-metrics v0.0.1 // indirect github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect - github.com/fatih/color v1.17.0 // indirect - github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-errors/errors v1.4.2 // indirect - github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/btree v1.1.2 // indirect github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/websocket v1.5.1 // indirect - github.com/gosuri/uitable v0.0.4 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect - github.com/gruntwork-io/go-commons v0.8.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-uuid v1.0.1 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/hpcloud/tail v1.0.0 // indirect - github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.16 // indirect - github.com/imkira/go-interpol v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect - github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/mattn/go-sqlite3 v1.14.19 // indirect - github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect - github.com/miekg/dns v1.1.62 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/locker v1.0.1 // indirect - github.com/moby/spdystream v0.4.0 // indirect - github.com/moby/term 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/moul/http2curl v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/pquerna/otp v1.2.0 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/rubenv/sql-migrate v1.5.2 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sanity-io/litter v1.5.5 // indirect - github.com/sergi/go-diff v1.3.1 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect - github.com/urfave/cli v1.22.14 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.34.0 // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xlab/treeprint v1.2.0 // indirect - github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect - github.com/yudai/gojsondiff v1.0.0 // indirect - github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - go.etcd.io/etcd/api/v3 v3.5.15 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect @@ -191,10 +84,9 @@ require ( go.opentelemetry.io/otel/sdk v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect - go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.6.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.24.0 // indirect @@ -207,22 +99,14 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.66.2 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect - gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.31.1 // indirect - k8s.io/cli-runtime v0.30.3 // indirect k8s.io/component-base v0.31.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect - k8s.io/kubectl v0.30.3 // indirect - moul.io/http2curl/v2 v2.3.0 // indirect - oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.17.2 // indirect - sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index f731d558c..02908cb7a 100644 --- a/go.sum +++ b/go.sum @@ -1,166 +1,40 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= -github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -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/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= -github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= -github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= github.com/agiledragon/gomonkey/v2 v2.10.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= -github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/api7/gopkg v0.2.1-0.20230601092738-0f3730f9b57a h1:YtrVQEEaw8/nu+qeGHwZq2DMVdEwHTTNsa5VvPZljSA= github.com/api7/gopkg v0.2.1-0.20230601092738-0f3730f9b57a/go.mod h1:kAbMwxeaiBpGsa1WvD2+UTq2tQBiT+ht5r5H/8onEDY= -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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.245 h1:KtY2s4q31/kn33AdV63R5t77mdxsI7rq3YT7Mgo805M= -github.com/aws/aws-sdk-go v1.44.245/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= -github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cch123/supermonkey v1.0.1 h1:sPNQhaqMpfpERGb1oNoPcYV5tGln72SLlG2q2ozpzqg= github.com/cch123/supermonkey v1.0.1/go.mod h1:d5jXTCyG6nu/pu0vYmoC0P/l0eBGesv3oQQ315uNBOA= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= -github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= -github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= -github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU= -github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU= -github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= -github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/gavv/httpexpect/v2 v2.16.0 h1:Ty2favARiTYTOkCRZGX7ojXXjGyNAIohM1lZ3vqaEwI= -github.com/gavv/httpexpect/v2 v2.16.0/go.mod h1:uJLaO+hQ25ukBJtQi750PsztObHybNllN+t+MbbW8PY= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= -github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -178,100 +52,39 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= -github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= -github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= -github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= -github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= -github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= -github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro= -github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= -github.com/gruntwork-io/terratest v0.47.0 h1:xIy1pT7NbGVlMLDZEHl3+3iSnvffh8tN2pL6idn448c= -github.com/gruntwork-io/terratest v0.47.0/go.mod h1:oywHw1cFKXSYvKPm27U7quZVzDUlA22H2xUrKCe26xM= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -279,50 +92,19 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= -github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/incubator4/go-resty-expr v0.1.1 h1:9ur1M+p0wDzL1bprdGzHugGkfK0Yd3Ba/ijcgvL+a1k= github.com/incubator4/go-resty-expr v0.1.1/go.mod h1:w9YQkQLUs1cArOb4O7SGJwJL/L8kuAo6y5CVS2o9eag= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -332,161 +114,47 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= -github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= -github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= -github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= -github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= -github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= -github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg= -github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= -github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= -github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -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/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw= github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= -github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= -github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= -github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= -github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= -github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/sony/sonyflake v1.1.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -494,70 +162,19 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= -github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4= -github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= -github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= -go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= -go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA= -go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU= -go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4= -go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= @@ -574,8 +191,6 @@ go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+ go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -588,112 +203,53 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.0.0-20200826200359-b19915210f00/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -702,65 +258,33 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= -gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -helm.sh/helm/v3 v3.15.4 h1:UFHd6oZ1IN3FsUZ7XNhOQDyQ2QYknBNWRHH57e9cbHY= -helm.sh/helm/v3 v3.15.4/go.mod h1:phOwlxqGSgppCY/ysWBNRhG3MtnpsttOzxaTK+Mt40E= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= @@ -769,8 +293,6 @@ k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= -k8s.io/cli-runtime v0.30.3 h1:aG69oRzJuP2Q4o8dm+f5WJIX4ZBEwrvdID0+MXyUY6k= -k8s.io/cli-runtime v0.30.3/go.mod h1:hwrrRdd9P84CXSKzhHxrOivAR9BRnkMt0OeP5mj7X30= k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= @@ -779,14 +301,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM= k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro= -k8s.io/kubectl v0.30.3 h1:YIBBvMdTW0xcDpmrOBzcpUVsn+zOgjMYIu7kAq+yqiI= -k8s.io/kubectl v0.30.3/go.mod h1:IcR0I9RN2+zzTRUa1BzZCm4oM0NLOawE6RzlDvd1Fpo= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= -moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= -oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= -oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= @@ -796,10 +312,6 @@ sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8= sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= -sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= -sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= -sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/internal/controller/gateway_controller.go b/internal/controller/gateway_controller.go index 332089ae9..4278e985b 100644 --- a/internal/controller/gateway_controller.go +++ b/internal/controller/gateway_controller.go @@ -18,6 +18,7 @@ import ( "fmt" "reflect" + "github.com/api7/gopkg/pkg/log" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -33,8 +34,6 @@ import ( gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1beta1" - "github.com/api7/gopkg/pkg/log" - "github.com/apache/apisix-ingress-controller/api/v1alpha1" "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/provider" diff --git a/internal/provider/adc/translator/gateway.go b/internal/provider/adc/translator/gateway.go index 2739e0278..943c0ee31 100644 --- a/internal/provider/adc/translator/gateway.go +++ b/internal/provider/adc/translator/gateway.go @@ -105,7 +105,7 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g Certificate: string(cert), Key: string(key), }) - // Dashboard doesn't allow wildcard hostname + // we doesn't allow wildcard hostname if listener.Hostname != nil && *listener.Hostname != "" { sslObj.Snis = append(sslObj.Snis, string(*listener.Hostname)) } @@ -118,7 +118,7 @@ func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener g continue } sslObj.Snis = append(sslObj.Snis, hosts...) - // Note: Dashboard doesn't allow duplicate certificate across ssl objects + // Note: use cert as id to avoid duplicate certificate across ssl objects sslObj.ID = id.GenID(string(cert)) log.Debugw("generated ssl id", zap.String("ssl id", sslObj.ID), zap.String("secret", secret.Namespace+"/"+secret.Name)) sslObj.Labels = label.GenLabel(obj) diff --git a/internal/provider/controlplane/controlplane.go b/internal/provider/controlplane/controlplane.go deleted file mode 100644 index 16d9f36aa..000000000 --- a/internal/provider/controlplane/controlplane.go +++ /dev/null @@ -1,183 +0,0 @@ -// 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 controlplane - -import ( - "context" - "fmt" - - "github.com/api7/gopkg/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/client" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/apache/apisix-ingress-controller/internal/controller/config" - "github.com/apache/apisix-ingress-controller/internal/provider" - "github.com/apache/apisix-ingress-controller/internal/provider/controlplane/translator" - "github.com/apache/apisix-ingress-controller/pkg/dashboard" -) - -type dashboardProvider struct { - translator *translator.Translator - c dashboard.Dashboard -} - -//nolint:unused -func NewDashboard() (provider.Provider, error) { - control, err := dashboard.NewClient() - if err != nil { - return nil, err - } - - if err := control.AddCluster(context.TODO(), &dashboard.ClusterOptions{ - Name: "default", - Labels: map[string]string{ - "k8s/controller-name": config.ControllerConfig.ControllerName, - }, - ControllerName: config.ControllerConfig.ControllerName, - SyncCache: true, - }); err != nil { - return nil, err - } - - return &dashboardProvider{ - translator: &translator.Translator{}, - c: control, - }, nil -} - -func (d *dashboardProvider) Update(ctx context.Context, tctx *provider.TranslateContext, obj client.Object) error { - var result *translator.TranslateResult - var err error - switch obj := obj.(type) { - case *gatewayv1.HTTPRoute: - result, err = d.translator.TranslateHTTPRoute(tctx, obj.DeepCopy()) - case *gatewayv1.Gateway: - result, err = d.translator.TranslateGateway(tctx, obj.DeepCopy()) - } - if err != nil { - return err - } - // TODO: support diff resources - name := "default" - for _, service := range result.Services { - if _, err := d.c.Cluster(name).Service().Update(ctx, service); err != nil { - return err - } - } - for _, route := range result.Routes { - if _, err := d.c.Cluster(name).Route().Update(ctx, route); err != nil { - return err - } - } - for _, ssl := range result.SSL { - // to avoid duplication - ssl.Snis = arrayUniqueElements(ssl.Snis, []string{}) - if len(ssl.Snis) == 1 && ssl.Snis[0] == "*" { - log.Warnf("wildcard hostname is not allowed in ssl object. Skipping SSL creation for %s: %s", obj.GetObjectKind().GroupVersionKind().Kind, obj.GetName()) - return nil - } - ssl.Snis = removeWildcard(ssl.Snis) - oldssl, err := d.c.Cluster(name).SSL().Get(ctx, ssl.Cert) - if err != nil || oldssl == nil { - if _, err := d.c.Cluster(name).SSL().Create(ctx, ssl); err != nil { - return fmt.Errorf("failed to create ssl for sni %+v: %w", ssl.Snis, err) - } - } else { - // array union is done to avoid host duplication - ssl.Snis = arrayUniqueElements(ssl.Snis, oldssl.Snis) - if _, err := d.c.Cluster(name).SSL().Update(ctx, ssl); err != nil { - return fmt.Errorf("failed to update ssl for sni %+v: %w", ssl.Snis, err) - } - } - } - return nil -} - -func removeWildcard(snis []string) []string { - newSni := make([]string, 0) - for _, sni := range snis { - if sni != "*" { - newSni = append(newSni, sni) - } - } - return newSni -} - -func arrayUniqueElements(arr1 []string, arr2 []string) []string { - // return a union of elements from both array - presentEle := make(map[string]bool) - newArr := make([]string, 0) - for _, ele := range arr1 { - if !presentEle[ele] { - presentEle[ele] = true - newArr = append(newArr, ele) - } - } - for _, ele := range arr2 { - if !presentEle[ele] { - presentEle[ele] = true - newArr = append(newArr, ele) - } - } - return newArr -} - -func (d *dashboardProvider) Delete(ctx context.Context, obj client.Object) error { - clusters := d.c.ListClusters() - kindLabel := dashboard.ListByKindLabelOptions{ - Kind: obj.GetObjectKind().GroupVersionKind().Kind, - Namespace: obj.GetNamespace(), - Name: obj.GetName(), - } - for _, cluster := range clusters { - switch obj.(type) { - case *gatewayv1.Gateway: - ssls, _ := cluster.SSL().List(ctx, dashboard.ListOptions{ - From: dashboard.ListFromCache, - KindLabel: kindLabel, - }) - for _, ssl := range ssls { - if err := cluster.SSL().Delete(ctx, ssl); err != nil { - return err - } - } - case *gatewayv1.HTTPRoute: - routes, _ := cluster.Route().List(ctx, dashboard.ListOptions{ - From: dashboard.ListFromCache, - KindLabel: kindLabel, - }) - - for _, route := range routes { - if err := cluster.Route().Delete(ctx, route); err != nil { - return err - } - } - - services, _ := cluster.Service().List(ctx, dashboard.ListOptions{ - From: dashboard.ListFromCache, - KindLabel: kindLabel, - }) - - for _, service := range services { - if err := cluster.Service().Delete(ctx, service); err != nil { - return err - } - } - } - } - return nil -} - -func (d *dashboardProvider) Sync(ctx context.Context) error { - return nil -} diff --git a/internal/provider/controlplane/manifest.go b/internal/provider/controlplane/manifest.go deleted file mode 100644 index 4848c1a82..000000000 --- a/internal/provider/controlplane/manifest.go +++ /dev/null @@ -1,13 +0,0 @@ -// 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 controlplane diff --git a/internal/provider/controlplane/translator/gateway.go b/internal/provider/controlplane/translator/gateway.go deleted file mode 100644 index 7aed6acd9..000000000 --- a/internal/provider/controlplane/translator/gateway.go +++ /dev/null @@ -1,161 +0,0 @@ -// 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 translator - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - - "github.com/api7/gopkg/pkg/log" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/internal/controller/label" - "github.com/apache/apisix-ingress-controller/internal/id" - "github.com/apache/apisix-ingress-controller/internal/provider" -) - -func (t *Translator) TranslateGateway(tctx *provider.TranslateContext, obj *gatewayv1.Gateway) (*TranslateResult, error) { - result := &TranslateResult{} - for _, listener := range obj.Spec.Listeners { - if listener.TLS != nil { - tctx.GatewayTLSConfig = append(tctx.GatewayTLSConfig, *listener.TLS) - ssl, err := t.translateSecret(tctx, listener, obj) - if err != nil { - return nil, fmt.Errorf("failed to translate secret: %w", err) - } - result.SSL = append(result.SSL, ssl...) - } - } - return result, nil -} - -func (t *Translator) translateSecret(tctx *provider.TranslateContext, listener gatewayv1.Listener, obj *gatewayv1.Gateway) ([]*v1.Ssl, error) { - if tctx.Secrets == nil { - return nil, nil - } - if listener.TLS.CertificateRefs == nil { - return nil, fmt.Errorf("no certificateRefs found in listener %s", listener.Name) - } - sslObjs := make([]*v1.Ssl, 0) - switch *listener.TLS.Mode { - case gatewayv1.TLSModeTerminate: - for _, ref := range listener.TLS.CertificateRefs { - ns := obj.GetNamespace() - if ref.Namespace != nil { - ns = string(*ref.Namespace) - } - if listener.TLS.CertificateRefs[0].Kind != nil && *listener.TLS.CertificateRefs[0].Kind == "Secret" { - sslObj := &v1.Ssl{ - Snis: []string{}, - } - name := listener.TLS.CertificateRefs[0].Name - secret := tctx.Secrets[types.NamespacedName{Namespace: ns, Name: string(ref.Name)}] - if secret == nil { - continue - } - if secret.Data == nil { - log.Error("secret data is nil", "secret", secret) - return nil, fmt.Errorf("no secret data found for %s/%s", ns, name) - } - cert, key, err := extractKeyPair(secret, true) - if err != nil { - return nil, err - } - sslObj.Cert = string(cert) - sslObj.Key = string(key) - // Dashboard doesn't allow wildcard hostname - if listener.Hostname != nil && *listener.Hostname != "" { - sslObj.Snis = append(sslObj.Snis, string(*listener.Hostname)) - } - hosts, err := extractHost(cert) - if err != nil { - return nil, err - } - sslObj.Snis = append(sslObj.Snis, hosts...) - // Note: Dashboard doesn't allow duplicate certificate across ssl objects - sslObj.ID = id.GenID(sslObj.Cert) - sslObj.Labels = label.GenLabel(obj) - sslObjs = append(sslObjs, sslObj) - } - - } - // Only supported on TLSRoute. The certificateRefs field is ignored in this mode. - case gatewayv1.TLSModePassthrough: - return sslObjs, nil - default: - return nil, fmt.Errorf("unknown TLS mode %s", *listener.TLS.Mode) - } - - return sslObjs, nil -} - -func extractHost(cert []byte) ([]string, error) { - block, _ := pem.Decode(cert) - if block == nil { - return nil, errors.New("parse certificate: not in PEM format") - } - der, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, errors.Wrap(err, "parse certificate") - } - return der.DNSNames, nil -} - -func extractKeyPair(s *corev1.Secret, hasPrivateKey bool) ([]byte, []byte, error) { - if _, ok := s.Data["cert"]; ok { - return extractApisixSecretKeyPair(s, hasPrivateKey) - } else if _, ok := s.Data[corev1.TLSCertKey]; ok { - return extractKubeSecretKeyPair(s, hasPrivateKey) - } else if ca, ok := s.Data[corev1.ServiceAccountRootCAKey]; ok && !hasPrivateKey { - return ca, nil, nil - } else { - return nil, nil, errors.New("unknown secret format") - } -} - -func extractApisixSecretKeyPair(s *corev1.Secret, hasPrivateKey bool) (cert []byte, key []byte, err error) { - var ok bool - cert, ok = s.Data["cert"] - if !ok { - return nil, nil, errors.New("missing cert field") - } - - if hasPrivateKey { - key, ok = s.Data["key"] - if !ok { - return nil, nil, errors.New("missing key field") - } - } - return -} - -func extractKubeSecretKeyPair(s *corev1.Secret, hasPrivateKey bool) (cert []byte, key []byte, err error) { - var ok bool - cert, ok = s.Data[corev1.TLSCertKey] - if !ok { - return nil, nil, errors.New("missing cert field") - } - - if hasPrivateKey { - key, ok = s.Data[corev1.TLSPrivateKeyKey] - if !ok { - return nil, nil, errors.New("missing key field") - } - } - return -} diff --git a/internal/provider/controlplane/translator/httproute.go b/internal/provider/controlplane/translator/httproute.go deleted file mode 100644 index 38bf6554a..000000000 --- a/internal/provider/controlplane/translator/httproute.go +++ /dev/null @@ -1,469 +0,0 @@ -// 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 translator - -import ( - "fmt" - "strings" - - "github.com/api7/gopkg/pkg/log" - "github.com/pkg/errors" - "go.uber.org/zap" - discoveryv1 "k8s.io/api/discovery/v1" - "k8s.io/apimachinery/pkg/types" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/internal/controller/label" - "github.com/apache/apisix-ingress-controller/internal/id" - "github.com/apache/apisix-ingress-controller/internal/provider" -) - -func (t *Translator) fillPluginsFromHTTPRouteFilters( - plugins v1.Plugins, - namespace string, - filters []gatewayv1.HTTPRouteFilter, - matches []gatewayv1.HTTPRouteMatch, - tctx *provider.TranslateContext, -) { - for _, filter := range filters { - switch filter.Type { - case gatewayv1.HTTPRouteFilterRequestHeaderModifier: - t.fillPluginFromHTTPRequestHeaderFilter(plugins, filter.RequestHeaderModifier) - case gatewayv1.HTTPRouteFilterRequestRedirect: - t.fillPluginFromHTTPRequestRedirectFilter(plugins, filter.RequestRedirect) - case gatewayv1.HTTPRouteFilterRequestMirror: - t.fillPluginFromHTTPRequestMirrorFilter(plugins, namespace, filter.RequestMirror) - case gatewayv1.HTTPRouteFilterURLRewrite: - t.fillPluginFromURLRewriteFilter(plugins, filter.URLRewrite, matches) - case gatewayv1.HTTPRouteFilterResponseHeaderModifier: - t.fillPluginFromHTTPResponseHeaderFilter(plugins, filter.ResponseHeaderModifier) - case gatewayv1.HTTPRouteFilterExtensionRef: - t.fillPluginFromExtensionRef(plugins, namespace, filter.ExtensionRef, tctx) - } - } -} - -func (t *Translator) fillPluginFromExtensionRef(plugins v1.Plugins, namespace string, extensionRef *gatewayv1.LocalObjectReference, tctx *provider.TranslateContext) { - if extensionRef == nil { - return - } - if extensionRef.Kind == "PluginConfig" { - pluginconfig := tctx.PluginConfigs[types.NamespacedName{ - Namespace: namespace, - Name: string(extensionRef.Name), - }] - for _, plugin := range pluginconfig.Spec.Plugins { - pluginName := plugin.Name - plugins[pluginName] = plugin.Config - log.Errorw("plugin config", zap.String("namespace", namespace), zap.Any("plugin_config", plugin)) - } - log.Errorw("plugin config", zap.String("namespace", namespace), zap.Any("plugins", plugins)) - } -} - -func (t *Translator) fillPluginFromURLRewriteFilter(plugins v1.Plugins, urlRewrite *gatewayv1.HTTPURLRewriteFilter, matches []gatewayv1.HTTPRouteMatch) { - pluginName := v1.PluginProxyRewrite - obj := plugins[pluginName] - var plugin *v1.RewriteConfig - if obj == nil { - plugin = &v1.RewriteConfig{} - plugins[pluginName] = plugin - } else { - plugin = obj.(*v1.RewriteConfig) - } - if urlRewrite.Hostname != nil { - plugin.Host = string(*urlRewrite.Hostname) - } - - if urlRewrite.Path != nil { - switch urlRewrite.Path.Type { - case gatewayv1.FullPathHTTPPathModifier: - plugin.RewriteTarget = *urlRewrite.Path.ReplaceFullPath - case gatewayv1.PrefixMatchHTTPPathModifier: - prefixPaths := make([]string, 0, len(matches)) - for _, match := range matches { - if match.Path == nil || match.Path.Type == nil || *match.Path.Type != gatewayv1.PathMatchPathPrefix { - continue - } - prefixPaths = append(prefixPaths, *match.Path.Value) - } - regexPattern := "^(" + strings.Join(prefixPaths, "|") + ")" + "/(.*)" - replaceTarget := *urlRewrite.Path.ReplacePrefixMatch - regexTarget := replaceTarget + "/$2" - - plugin.RewriteTargetRegex = []string{ - regexPattern, - regexTarget, - } - } - } -} - -func (t *Translator) fillPluginFromHTTPRequestHeaderFilter(plugins v1.Plugins, reqHeaderModifier *gatewayv1.HTTPHeaderFilter) { - pluginName := v1.PluginProxyRewrite - obj := plugins[pluginName] - var plugin *v1.RewriteConfig - if obj == nil { - plugin = &v1.RewriteConfig{ - Headers: &v1.Headers{ - Add: make(map[string]string, len(reqHeaderModifier.Add)), - Set: make(map[string]string, len(reqHeaderModifier.Set)), - Remove: make([]string, 0, len(reqHeaderModifier.Remove)), - }, - } - plugins[pluginName] = plugin - } else { - plugin = obj.(*v1.RewriteConfig) - } - for _, header := range reqHeaderModifier.Add { - val := plugin.Headers.Add[string(header.Name)] - if val != "" { - val += ", " + header.Value - } else { - val = header.Value - } - plugin.Headers.Add[string(header.Name)] = val - } - for _, header := range reqHeaderModifier.Set { - plugin.Headers.Set[string(header.Name)] = header.Value - } - plugin.Headers.Remove = append(plugin.Headers.Remove, reqHeaderModifier.Remove...) -} - -func (t *Translator) fillPluginFromHTTPResponseHeaderFilter(plugins v1.Plugins, respHeaderModifier *gatewayv1.HTTPHeaderFilter) { - pluginName := v1.PluginResponseRewrite - obj := plugins[pluginName] - var plugin *v1.ResponseRewriteConfig - if obj == nil { - plugin = &v1.ResponseRewriteConfig{ - Headers: &v1.ResponseHeaders{ - Add: make([]string, 0, len(respHeaderModifier.Add)), - Set: make(map[string]string, len(respHeaderModifier.Set)), - Remove: make([]string, 0, len(respHeaderModifier.Remove)), - }, - } - plugins[pluginName] = plugin - } else { - plugin = obj.(*v1.ResponseRewriteConfig) - } - for _, header := range respHeaderModifier.Add { - plugin.Headers.Add = append(plugin.Headers.Add, fmt.Sprintf("%s: %s", header.Name, header.Value)) - } - for _, header := range respHeaderModifier.Set { - plugin.Headers.Set[string(header.Name)] = header.Value - } - plugin.Headers.Remove = append(plugin.Headers.Remove, respHeaderModifier.Remove...) -} - -func (t *Translator) fillPluginFromHTTPRequestMirrorFilter(plugins v1.Plugins, namespace string, reqMirror *gatewayv1.HTTPRequestMirrorFilter) { - pluginName := v1.PluginProxyMirror - obj := plugins[pluginName] - - var plugin *v1.RequestMirror - if obj == nil { - plugin = &v1.RequestMirror{} - plugins[pluginName] = plugin - } else { - plugin = obj.(*v1.RequestMirror) - } - - var ( - port = 80 - ns = namespace - ) - if reqMirror.BackendRef.Port != nil { - port = int(*reqMirror.BackendRef.Port) - } - if reqMirror.BackendRef.Namespace != nil { - ns = string(*reqMirror.BackendRef.Namespace) - } - - host := fmt.Sprintf("http://%s.%s.svc.cluster.local:%d", reqMirror.BackendRef.Name, ns, port) - - plugin.Host = host -} - -func (t *Translator) fillPluginFromHTTPRequestRedirectFilter(plugins v1.Plugins, reqRedirect *gatewayv1.HTTPRequestRedirectFilter) { - pluginName := v1.PluginRedirect - obj := plugins[pluginName] - - var plugin *v1.RedirectConfig - if obj == nil { - plugin = &v1.RedirectConfig{} - plugins[pluginName] = plugin - } else { - plugin = obj.(*v1.RedirectConfig) - } - var uri string - - code := 302 - if reqRedirect.StatusCode != nil { - code = *reqRedirect.StatusCode - } - - hostname := "$host" - if reqRedirect.Hostname != nil { - hostname = string(*reqRedirect.Hostname) - } - - scheme := "$scheme" - if reqRedirect.Scheme != nil { - scheme = *reqRedirect.Scheme - } - - if reqRedirect.Port != nil { - uri = fmt.Sprintf("%s://%s:%d$request_uri", scheme, hostname, int(*reqRedirect.Port)) - } else { - uri = fmt.Sprintf("%s://%s$request_uri", scheme, hostname) - } - plugin.RetCode = code - plugin.URI = uri -} - -func (t *Translator) translateEndpointSlice(endpointSlices []discoveryv1.EndpointSlice) v1.UpstreamNodes { - var nodes v1.UpstreamNodes - if len(endpointSlices) == 0 { - return nodes - } - for _, endpointSlice := range endpointSlices { - for _, port := range endpointSlice.Ports { - for _, endpoint := range endpointSlice.Endpoints { - for _, addr := range endpoint.Addresses { - node := v1.UpstreamNode{ - Host: addr, - Port: int(*port.Port), - Weight: 1, - } - nodes = append(nodes, node) - } - } - } - } - - return nodes -} - -func (t *Translator) translateBackendRef(tctx *provider.TranslateContext, ref gatewayv1.BackendRef) *v1.Upstream { - upstream := v1.NewDefaultUpstream() - endpointSlices := tctx.EndpointSlices[types.NamespacedName{ - Namespace: string(*ref.Namespace), - Name: string(ref.Name), - }] - - upstream.Nodes = t.translateEndpointSlice(endpointSlices) - return upstream -} - -func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRoute *gatewayv1.HTTPRoute) (*TranslateResult, error) { - result := &TranslateResult{} - - hosts := make([]string, 0, len(httpRoute.Spec.Hostnames)) - for _, hostname := range httpRoute.Spec.Hostnames { - hosts = append(hosts, string(hostname)) - } - - rules := httpRoute.Spec.Rules - - for i, rule := range rules { - - var weightedUpstreams []v1.TrafficSplitConfigRuleWeightedUpstream - upstreams := []*v1.Upstream{} - for _, backend := range rule.BackendRefs { - if backend.Namespace == nil { - namespace := gatewayv1.Namespace(httpRoute.Namespace) - backend.Namespace = &namespace - } - upstream := t.translateBackendRef(tctx, backend.BackendRef) - upstream.Labels["name"] = string(backend.Name) - upstream.Labels["namespace"] = string(*backend.Namespace) - upstreams = append(upstreams, upstream) - if len(upstream.Nodes) == 0 { - upstream.Nodes = v1.UpstreamNodes{ - { - Host: "0.0.0.0", - Port: 80, - Weight: 100, - }, - } - } - - weight := 100 - if backend.Weight != nil { - weight = int(*backend.Weight) - } - weightedUpstreams = append(weightedUpstreams, v1.TrafficSplitConfigRuleWeightedUpstream{ - Upstream: upstream, - Weight: weight, - }) - } - - if len(upstreams) == 0 { - upstream := v1.NewDefaultUpstream() - upstream.Nodes = v1.UpstreamNodes{ - { - Host: "0.0.0.0", - Port: 80, - Weight: 100, - }, - } - upstreams = append(upstreams, upstream) - } - - service := v1.NewDefaultService() - service.Upstream = upstreams[0] - if len(weightedUpstreams) > 1 { - weightedUpstreams[0].Upstream = nil - service.Plugins["traffic-split"] = &v1.TrafficSplitConfig{ - Rules: []v1.TrafficSplitConfigRule{ - { - WeightedUpstreams: weightedUpstreams, - }, - }, - } - } - - service.Name = v1.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", i)) - service.ID = id.GenID(service.Name) - service.Labels = label.GenLabel(httpRoute) - service.Hosts = hosts - t.fillPluginsFromHTTPRouteFilters(service.Plugins, httpRoute.GetNamespace(), rule.Filters, rule.Matches, tctx) - - result.Services = append(result.Services, service) - - matches := rule.Matches - if len(matches) == 0 { - defaultType := gatewayv1.PathMatchPathPrefix - defaultValue := "/" - matches = []gatewayv1.HTTPRouteMatch{ - { - Path: &gatewayv1.HTTPPathMatch{ - Type: &defaultType, - Value: &defaultValue, - }, - }, - } - } - - for j, match := range matches { - route, err := t.translateGatewayHTTPRouteMatch(&match) - if err != nil { - return nil, err - } - - name := v1.ComposeRouteName(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d-%d", i, j)) - route.Name = name - route.ID = id.GenID(name) - route.Labels = label.GenLabel(httpRoute) - route.ServiceID = service.ID - result.Routes = append(result.Routes, route) - } - } - - return result, nil -} - -func (t *Translator) translateGatewayHTTPRouteMatch(match *gatewayv1.HTTPRouteMatch) (*v1.Route, error) { - route := v1.NewDefaultRoute() - - if match.Path != nil { - switch *match.Path.Type { - case gatewayv1.PathMatchExact: - route.Paths = []string{*match.Path.Value} - case gatewayv1.PathMatchPathPrefix: - route.Paths = []string{*match.Path.Value + "*"} - case gatewayv1.PathMatchRegularExpression: - var this []v1.StringOrSlice - this = append(this, v1.StringOrSlice{ - StrVal: "uri", - }) - this = append(this, v1.StringOrSlice{ - StrVal: "~~", - }) - this = append(this, v1.StringOrSlice{ - StrVal: *match.Path.Value, - }) - - route.Vars = append(route.Vars, this) - default: - return nil, errors.New("unknown path match type " + string(*match.Path.Type)) - } - } - - if len(match.Headers) > 0 { - for _, header := range match.Headers { - name := strings.ToLower(string(header.Name)) - name = strings.ReplaceAll(name, "-", "_") - - var this []v1.StringOrSlice - this = append(this, v1.StringOrSlice{ - StrVal: "http_" + name, - }) - - switch *header.Type { - case gatewayv1.HeaderMatchExact: - this = append(this, v1.StringOrSlice{ - StrVal: "==", - }) - case gatewayv1.HeaderMatchRegularExpression: - this = append(this, v1.StringOrSlice{ - StrVal: "~~", - }) - default: - return nil, errors.New("unknown header match type " + string(*header.Type)) - } - - this = append(this, v1.StringOrSlice{ - StrVal: header.Value, - }) - - route.Vars = append(route.Vars, this) - } - } - - if len(match.QueryParams) > 0 { - for _, query := range match.QueryParams { - var this []v1.StringOrSlice - this = append(this, v1.StringOrSlice{ - StrVal: "arg_" + strings.ToLower(fmt.Sprintf("%v", query.Name)), - }) - - switch *query.Type { - case gatewayv1.QueryParamMatchExact: - this = append(this, v1.StringOrSlice{ - StrVal: "==", - }) - case gatewayv1.QueryParamMatchRegularExpression: - this = append(this, v1.StringOrSlice{ - StrVal: "~~", - }) - default: - return nil, errors.New("unknown query match type " + string(*query.Type)) - } - - this = append(this, v1.StringOrSlice{ - StrVal: query.Value, - }) - - route.Vars = append(route.Vars, this) - } - } - - if match.Method != nil { - route.Methods = []string{ - string(*match.Method), - } - } - - return route, nil -} diff --git a/internal/provider/controlplane/translator/translator.go b/internal/provider/controlplane/translator/translator.go deleted file mode 100644 index 31bb67167..000000000 --- a/internal/provider/controlplane/translator/translator.go +++ /dev/null @@ -1,50 +0,0 @@ -// 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 translator - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - discoveryv1 "k8s.io/api/discovery/v1" - "k8s.io/apimachinery/pkg/types" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/api/v1alpha1" -) - -type Translator struct { - Log logr.Logger -} - -type TranslateContext struct { - BackendRefs []gatewayv1.BackendRef - GatewayTLSConfig []gatewayv1.GatewayTLSConfig - EndpointSlices map[types.NamespacedName][]discoveryv1.EndpointSlice - Secrets map[types.NamespacedName]*corev1.Secret - PluginConfigs map[types.NamespacedName]*v1alpha1.PluginConfig -} - -type TranslateResult struct { - Routes []*v1.Route - Services []*v1.Service - SSL []*v1.Ssl -} - -func NewDefaultTranslateContext() *TranslateContext { - return &TranslateContext{ - EndpointSlices: make(map[types.NamespacedName][]discoveryv1.EndpointSlice), - Secrets: make(map[types.NamespacedName]*corev1.Secret), - PluginConfigs: make(map[types.NamespacedName]*v1alpha1.PluginConfig), - } -} diff --git a/pkg/dashboard/cache/cache.go b/pkg/dashboard/cache/cache.go deleted file mode 100644 index b34786d4b..000000000 --- a/pkg/dashboard/cache/cache.go +++ /dev/null @@ -1,92 +0,0 @@ -// 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 cache - -import v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - -// Cache defines the necessary behaviors that the cache object should have. -// Note this interface is for APISIX, not for generic purpose, it supports -// standard APISIX resources, i.e. Route, Upstream, and SSL. -// Cache implementations should copy the target objects before/after read/write -// operations for the sake of avoiding data corrupted by other writers. -type Cache interface { - // InsertRoute adds or updates route to cache. - InsertRoute(*v1.Route) error - // InsertStreamRoute adds or updates stream_route to cache. - InsertStreamRoute(*v1.StreamRoute) error - // InsertSSL adds or updates ssl to cache. - InsertSSL(*v1.Ssl) error - // InsertUpstream adds or updates upstream to cache. - InsertService(*v1.Service) error - // InsertGlobalRule adds or updates global_rule to cache. - InsertGlobalRule(*v1.GlobalRule) error - // InsertConsumer adds or updates consumer to cache. - InsertConsumer(*v1.Consumer) error - // InsertSchema adds or updates schema to cache. - InsertSchema(*v1.Schema) error - // InsertPluginConfig adds or updates plugin_config to cache. - InsertPluginConfig(*v1.PluginConfig) error - - // GetRoute finds the route from cache according to the primary index (id). - GetRoute(string) (*v1.Route, error) - GetStreamRoute(string) (*v1.StreamRoute, error) - // GetSSL finds the ssl from cache according to the primary index (id). - GetSSL(string) (*v1.Ssl, error) - // GetUpstream finds the upstream from cache according to the primary index (id). - GetService(string) (*v1.Service, error) - // GetGlobalRule finds the global_rule from cache according to the primary index (id). - GetGlobalRule(string) (*v1.GlobalRule, error) - // GetConsumer finds the consumer from cache according to the primary index (username). - GetConsumer(string) (*v1.Consumer, error) - // GetSchema finds the scheme from cache according to the primary index (name). - GetSchema(string) (*v1.Schema, error) - // GetPluginConfig finds the plugin_config from cache according to the primary index (id). - GetPluginConfig(string) (*v1.PluginConfig, error) - - // ListRoutes lists all routes in cache. - ListRoutes(...any) ([]*v1.Route, error) - // ListStreamRoutes lists all stream_route objects in cache. - ListStreamRoutes() ([]*v1.StreamRoute, error) - // ListSSL lists all ssl objects in cache. - ListSSL(...any) ([]*v1.Ssl, error) - // ListUpstreams lists all upstreams in cache. - ListServices(...any) ([]*v1.Service, error) - // ListGlobalRules lists all global_rule objects in cache. - ListGlobalRules() ([]*v1.GlobalRule, error) - // ListConsumers lists all consumer objects in cache. - ListConsumers() ([]*v1.Consumer, error) - // ListSchema lists all schema in cache. - ListSchema() ([]*v1.Schema, error) - // ListPluginConfigs lists all plugin_config in cache. - ListPluginConfigs() ([]*v1.PluginConfig, error) - - // DeleteRoute deletes the specified route in cache. - DeleteRoute(*v1.Route) error - // DeleteStreamRoute deletes the specified stream_route in cache. - DeleteStreamRoute(*v1.StreamRoute) error - // DeleteSSL deletes the specified ssl in cache. - DeleteSSL(*v1.Ssl) error - // DeleteUpstream deletes the specified upstream in cache. - DeleteService(*v1.Service) error - // DeleteGlobalRule deletes the specified stream_route in cache. - DeleteGlobalRule(*v1.GlobalRule) error - // DeleteConsumer deletes the specified consumer in cache. - DeleteConsumer(*v1.Consumer) error - // DeleteSchema deletes the specified schema in cache. - DeleteSchema(*v1.Schema) error - // DeletePluginConfig deletes the specified plugin_config in cache. - DeletePluginConfig(*v1.PluginConfig) error - - CheckServiceReference(*v1.Service) error - CheckPluginConfigReference(*v1.PluginConfig) error -} diff --git a/pkg/dashboard/cache/memdb.go b/pkg/dashboard/cache/memdb.go deleted file mode 100644 index 34ce7a111..000000000 --- a/pkg/dashboard/cache/memdb.go +++ /dev/null @@ -1,363 +0,0 @@ -// 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 cache - -import ( - "errors" - - "github.com/hashicorp/go-memdb" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -var ( - // ErrStillInUse means an object is still in use. - ErrStillInUse = errors.New("still in use") - // ErrNotFound is returned when the requested item is not found. - ErrNotFound = memdb.ErrNotFound -) - -type dbCache struct { - db *memdb.MemDB -} - -// NewMemDBCache creates a Cache object backs with a memory DB. -func NewMemDBCache() (Cache, error) { - db, err := memdb.NewMemDB(_schema) - if err != nil { - return nil, err - } - return &dbCache{ - db: db, - }, nil -} - -func (c *dbCache) InsertRoute(r *v1.Route) error { - route := r.DeepCopy() - return c.insert("route", route) -} - -func (c *dbCache) InsertSSL(ssl *v1.Ssl) error { - return c.insert("ssl", ssl.DeepCopy()) -} - -func (c *dbCache) InsertService(u *v1.Service) error { - return c.insert("service", u.DeepCopy()) -} - -func (c *dbCache) InsertGlobalRule(gr *v1.GlobalRule) error { - return c.insert("global_rule", gr.DeepCopy()) -} - -func (c *dbCache) InsertConsumer(consumer *v1.Consumer) error { - return c.insert("consumer", consumer.DeepCopy()) -} -func (c *dbCache) InsertStreamRoute(sr *v1.StreamRoute) error { - return c.insert("stream_route", sr.DeepCopy()) -} - -func (c *dbCache) InsertSchema(schema *v1.Schema) error { - return c.insert("schema", schema.DeepCopy()) -} - -func (c *dbCache) InsertPluginConfig(pc *v1.PluginConfig) error { - return c.insert("plugin_config", pc.DeepCopy()) -} - -func (c *dbCache) insert(table string, obj any) error { - txn := c.db.Txn(true) - defer txn.Abort() - if err := txn.Insert(table, obj); err != nil { - return err - } - txn.Commit() - return nil -} - -func (c *dbCache) GetRoute(id string) (*v1.Route, error) { - obj, err := c.get("route", id) - if err != nil { - return nil, err - } - return obj.(*v1.Route).DeepCopy(), nil -} - -func (c *dbCache) GetSSL(id string) (*v1.Ssl, error) { - obj, err := c.get("ssl", id) - if err != nil { - return nil, err - } - return obj.(*v1.Ssl).DeepCopy(), nil -} - -func (c *dbCache) GetService(id string) (*v1.Service, error) { - obj, err := c.get("service", id) - if err != nil { - return nil, err - } - return obj.(*v1.Service).DeepCopy(), nil -} - -func (c *dbCache) GetGlobalRule(id string) (*v1.GlobalRule, error) { - obj, err := c.get("global_rule", id) - if err != nil { - return nil, err - } - return obj.(*v1.GlobalRule).DeepCopy(), nil -} - -func (c *dbCache) GetConsumer(username string) (*v1.Consumer, error) { - obj, err := c.get("consumer", username) - if err != nil { - return nil, err - } - return obj.(*v1.Consumer).DeepCopy(), nil -} - -func (c *dbCache) GetStreamRoute(id string) (*v1.StreamRoute, error) { - obj, err := c.get("stream_route", id) - if err != nil { - return nil, err - } - return obj.(*v1.StreamRoute).DeepCopy(), nil -} - -func (c *dbCache) GetSchema(name string) (*v1.Schema, error) { - obj, err := c.get("schema", name) - if err != nil { - return nil, err - } - return obj.(*v1.Schema).DeepCopy(), nil -} - -func (c *dbCache) GetPluginConfig(name string) (*v1.PluginConfig, error) { - obj, err := c.get("plugin_config", name) - if err != nil { - return nil, err - } - return obj.(*v1.PluginConfig).DeepCopy(), nil -} - -func (c *dbCache) get(table, id string) (any, error) { - txn := c.db.Txn(false) - defer txn.Abort() - obj, err := txn.First(table, "id", id) - if err != nil { - if err == memdb.ErrNotFound { - return nil, ErrNotFound - } - return nil, err - } - if obj == nil { - return nil, ErrNotFound - } - return obj, nil -} - -func (c *dbCache) ListRoutes(args ...any) ([]*v1.Route, error) { - raws, err := c.list("route", args...) - if err != nil { - return nil, err - } - routes := make([]*v1.Route, 0, len(raws)) - for _, raw := range raws { - routes = append(routes, raw.(*v1.Route).DeepCopy()) - } - return routes, nil -} - -func (c *dbCache) ListSSL(args ...any) ([]*v1.Ssl, error) { - raws, err := c.list("ssl", args...) - if err != nil { - return nil, err - } - ssl := make([]*v1.Ssl, 0, len(raws)) - for _, raw := range raws { - ssl = append(ssl, raw.(*v1.Ssl).DeepCopy()) - } - return ssl, nil -} - -func (c *dbCache) ListServices(args ...any) ([]*v1.Service, error) { - raws, err := c.list("service", args...) - if err != nil { - return nil, err - } - services := make([]*v1.Service, 0, len(raws)) - for _, raw := range raws { - services = append(services, raw.(*v1.Service).DeepCopy()) - } - return services, nil -} - -func (c *dbCache) ListGlobalRules() ([]*v1.GlobalRule, error) { - raws, err := c.list("global_rule") - if err != nil { - return nil, err - } - globalRules := make([]*v1.GlobalRule, 0, len(raws)) - for _, raw := range raws { - globalRules = append(globalRules, raw.(*v1.GlobalRule).DeepCopy()) - } - return globalRules, nil -} - -func (c *dbCache) ListStreamRoutes() ([]*v1.StreamRoute, error) { - raws, err := c.list("stream_route") - if err != nil { - return nil, err - } - streamRoutes := make([]*v1.StreamRoute, 0, len(raws)) - for _, raw := range raws { - streamRoutes = append(streamRoutes, raw.(*v1.StreamRoute).DeepCopy()) - } - return streamRoutes, nil -} - -func (c *dbCache) ListConsumers() ([]*v1.Consumer, error) { - raws, err := c.list("consumer") - if err != nil { - return nil, err - } - consumers := make([]*v1.Consumer, 0, len(raws)) - for _, raw := range raws { - consumers = append(consumers, raw.(*v1.Consumer).DeepCopy()) - } - return consumers, nil -} - -func (c *dbCache) ListSchema() ([]*v1.Schema, error) { - raws, err := c.list("schema") - if err != nil { - return nil, err - } - schemaList := make([]*v1.Schema, 0, len(raws)) - for _, raw := range raws { - schemaList = append(schemaList, raw.(*v1.Schema).DeepCopy()) - } - return schemaList, nil -} - -func (c *dbCache) ListPluginConfigs() ([]*v1.PluginConfig, error) { - raws, err := c.list("plugin_config") - if err != nil { - return nil, err - } - pluginConfigs := make([]*v1.PluginConfig, 0, len(raws)) - for _, raw := range raws { - pluginConfigs = append(pluginConfigs, raw.(*v1.PluginConfig).DeepCopy()) - } - return pluginConfigs, nil -} - -func (c *dbCache) list(table string, args ...any) ([]any, error) { - txn := c.db.Txn(false) - defer txn.Abort() - index := "id" - if len(args) > 0 { - idx, ok := args[0].(string) - if !ok { - return nil, errors.New("unexpected index type") - } - index = idx - args = args[1:] - } - iter, err := txn.Get(table, index, args...) - if err != nil { - return nil, err - } - var objs []any - for obj := iter.Next(); obj != nil; obj = iter.Next() { - objs = append(objs, obj) - } - return objs, nil -} - -func (c *dbCache) DeleteRoute(r *v1.Route) error { - return c.delete("route", r) -} - -func (c *dbCache) DeleteSSL(ssl *v1.Ssl) error { - return c.delete("ssl", ssl) -} - -func (c *dbCache) DeleteService(u *v1.Service) error { - if err := c.CheckServiceReference(u); err != nil { - return err - } - return c.delete("service", u) -} - -func (c *dbCache) DeleteStreamRoute(sr *v1.StreamRoute) error { - return c.delete("stream_route", sr) -} - -func (c *dbCache) DeleteGlobalRule(gr *v1.GlobalRule) error { - return c.delete("global_rule", gr) -} - -func (c *dbCache) DeleteConsumer(consumer *v1.Consumer) error { - return c.delete("consumer", consumer) -} - -func (c *dbCache) DeleteSchema(schema *v1.Schema) error { - return c.delete("schema", schema) -} - -func (c *dbCache) DeletePluginConfig(pc *v1.PluginConfig) error { - if err := c.CheckPluginConfigReference(pc); err != nil { - return err - } - return c.delete("plugin_config", pc) -} - -func (c *dbCache) delete(table string, obj any) error { - txn := c.db.Txn(true) - defer txn.Abort() - if err := txn.Delete(table, obj); err != nil { - if err == memdb.ErrNotFound { - return ErrNotFound - } - return err - } - txn.Commit() - return nil -} - -func (c *dbCache) CheckServiceReference(u *v1.Service) error { - // Upstream is referenced by Route. - txn := c.db.Txn(false) - defer txn.Abort() - obj, err := txn.First("route", "service_id", u.ID) - if err != nil && err != memdb.ErrNotFound { - return err - } - if obj != nil { - return ErrStillInUse - } - return nil -} - -func (c *dbCache) CheckPluginConfigReference(u *v1.PluginConfig) error { - // PluginConfig is referenced by Route. - txn := c.db.Txn(false) - defer txn.Abort() - obj, err := txn.First("route", "plugin_config_id", u.ID) - if err != nil && err != memdb.ErrNotFound { - return err - } - if obj != nil { - return ErrStillInUse - } - return nil -} diff --git a/pkg/dashboard/cache/memdb_test.go b/pkg/dashboard/cache/memdb_test.go deleted file mode 100644 index a4c86252e..000000000 --- a/pkg/dashboard/cache/memdb_test.go +++ /dev/null @@ -1,385 +0,0 @@ -// 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 cache - -import ( - "testing" - - "github.com/hashicorp/go-memdb" - "github.com/stretchr/testify/assert" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -func TestMemDBCacheRoute(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - r1 := &v1.Route{ - Metadata: v1.Metadata{ - ID: "1", - Name: "abc", - }, - } - assert.Nil(t, c.InsertRoute(r1), "inserting route 1") - - r, err := c.GetRoute("1") - assert.Nil(t, err) - assert.Equal(t, r1, r) - - r2 := &v1.Route{ - Metadata: v1.Metadata{ - ID: "2", - Name: "def", - }, - } - r3 := &v1.Route{ - Metadata: v1.Metadata{ - ID: "3", - Name: "ghi", - }, - } - assert.Nil(t, c.InsertRoute(r2), "inserting route r2") - assert.Nil(t, c.InsertRoute(r3), "inserting route r3") - - r, err = c.GetRoute("3") - assert.Nil(t, err) - assert.Equal(t, r3, r) - - assert.Nil(t, c.DeleteRoute(r3), "delete route r3") - - routes, err := c.ListRoutes() - assert.Nil(t, err, "listing routes") - - if routes[0].Name > routes[1].Name { - routes[0], routes[1] = routes[1], routes[0] - } - assert.Equal(t, r1, routes[0]) - assert.Equal(t, r2, routes[1]) - - r4 := &v1.Route{ - Metadata: v1.Metadata{ - ID: "4", - Name: "name4", - }, - } - assert.Error(t, ErrNotFound, c.DeleteRoute(r4)) -} - -func TestMemDBCacheSSL(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - s1 := &v1.Ssl{ - ID: "abc", - } - assert.Nil(t, c.InsertSSL(s1), "inserting ssl 1") - - s, err := c.GetSSL("abc") - assert.Nil(t, err) - assert.Equal(t, s1, s) - - s2 := &v1.Ssl{ - ID: "def", - } - s3 := &v1.Ssl{ - ID: "ghi", - } - assert.Nil(t, c.InsertSSL(s2), "inserting ssl 2") - assert.Nil(t, c.InsertSSL(s3), "inserting ssl 3") - - s, err = c.GetSSL("ghi") - assert.Nil(t, err) - assert.Equal(t, s3, s) - - assert.Nil(t, c.DeleteSSL(s3), "delete ssl 3") - - ssl, err := c.ListSSL() - assert.Nil(t, err, "listing ssl") - - if ssl[0].ID > ssl[1].ID { - ssl[0], ssl[1] = ssl[1], ssl[0] - } - assert.Equal(t, s1, ssl[0]) - assert.Equal(t, s2, ssl[1]) - - s4 := &v1.Ssl{ - ID: "id4", - } - assert.Error(t, ErrNotFound, c.DeleteSSL(s4)) -} - -func TestMemDBCacheUpstream(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - u1 := &v1.Service{ - Metadata: v1.Metadata{ - ID: "1", - Name: "abc", - }, - } - err = c.InsertService(u1) - assert.Nil(t, err, "inserting upstream 1") - - u, err := c.GetService("1") - assert.Nil(t, err) - assert.Equal(t, u1, u) - - u2 := &v1.Service{ - Metadata: v1.Metadata{ - Name: "def", - ID: "2", - }, - } - u3 := &v1.Service{ - Metadata: v1.Metadata{ - Name: "ghi", - ID: "3", - }, - } - assert.Nil(t, c.InsertService(u2), "inserting upstream 2") - assert.Nil(t, c.InsertService(u3), "inserting upstream 3") - - u, err = c.GetService("3") - assert.Nil(t, err) - assert.Equal(t, u3, u) - - assert.Nil(t, c.DeleteService(u3), "delete upstream 3") - - upstreams, err := c.ListServices() - assert.Nil(t, err, "listing upstreams") - - if upstreams[0].Name > upstreams[1].Name { - upstreams[0], upstreams[1] = upstreams[1], upstreams[0] - } - assert.Equal(t, u1, upstreams[0]) - assert.Equal(t, u2, upstreams[1]) - - u4 := &v1.Service{ - Metadata: v1.Metadata{ - Name: "name4", - ID: "4", - }, - } - assert.Error(t, ErrNotFound, c.DeleteService(u4)) -} - -func TestMemDBCacheReference(t *testing.T) { - r := &v1.Route{ - Metadata: v1.Metadata{ - Name: "route", - ID: "1", - }, - ServiceID: "1", - PluginConfigId: "1", - } - u := &v1.Service{ - Metadata: v1.Metadata{ - ID: "1", - Name: "upstream", - }, - } - pc := &v1.PluginConfig{ - Metadata: v1.Metadata{ - ID: "1", - Name: "pluginConfig", - }, - } - pc2 := &v1.PluginConfig{ - Metadata: v1.Metadata{ - ID: "2", - Name: "pluginConfig", - }, - } - - db, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - assert.Nil(t, db.InsertRoute(r)) - assert.Nil(t, db.InsertService(u)) - assert.Nil(t, db.InsertPluginConfig(pc)) - - assert.Error(t, ErrStillInUse, db.DeleteService(u)) - assert.Error(t, ErrStillInUse, db.DeletePluginConfig(pc)) - assert.Equal(t, memdb.ErrNotFound, db.DeletePluginConfig(pc2)) - assert.Nil(t, db.DeleteRoute(r)) - assert.Nil(t, db.DeleteService(u)) - assert.Nil(t, db.DeletePluginConfig(pc)) -} - -func testInsertAndGetGlobalRule(t *testing.T, c Cache, id string) { - gr1 := &v1.GlobalRule{ - ID: id, - } - assert.Nil(t, c.InsertGlobalRule(gr1), "inserting global rule "+id) - - gr, err := c.GetGlobalRule(id) - assert.Nil(t, err) - assert.Equal(t, gr1, gr) -} - -func TestMemDBCacheGlobalRule(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - testInsertAndGetGlobalRule(t, c, "1") - testInsertAndGetGlobalRule(t, c, "2") - testInsertAndGetGlobalRule(t, c, "3") - - grs, err := c.ListGlobalRules() - assert.Nil(t, err, "listing global rules") - assert.Len(t, grs, 3) - assert.ElementsMatch(t, []string{"1", "2", "3"}, []string{grs[0].ID, grs[1].ID, grs[2].ID}) - - assert.Error(t, ErrNotFound, c.DeleteGlobalRule(&v1.GlobalRule{ - ID: "4", - })) -} - -func testInsertAndGetConsumer(t *testing.T, c Cache, username string) { - c1 := &v1.Consumer{ - Username: username, - } - assert.Nil(t, c.InsertConsumer(c1), "inserting consumer "+username) - - c11, err := c.GetConsumer(username) - assert.Nil(t, err) - assert.Equal(t, c1, c11) -} - -func TestMemDBCacheConsumer(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - testInsertAndGetConsumer(t, c, "jack") - testInsertAndGetConsumer(t, c, "tom") - testInsertAndGetConsumer(t, c, "jerry") - consumers, err := c.ListConsumers() - assert.Nil(t, err, "listing consumers") - assert.Len(t, consumers, 3) - - assert.Nil(t, c.DeleteConsumer( - &v1.Consumer{ - Username: "jerry", - }), "delete consumer jerry") - - consumers, err = c.ListConsumers() - assert.Nil(t, err, "listing consumers") - assert.Len(t, consumers, 2) - assert.ElementsMatch(t, []string{"jack", "tom"}, []string{consumers[0].Username, consumers[1].Username}) - - assert.Error(t, ErrNotFound, c.DeleteConsumer( - &v1.Consumer{ - Username: "chandler", - }, - )) -} - -func TestMemDBCacheSchema(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - s1 := &v1.Schema{ - Name: "plugins/p1", - Content: "plugin schema", - } - assert.Nil(t, c.InsertSchema(s1), "inserting schema s1") - - s11, err := c.GetSchema("plugins/p1") - assert.Nil(t, err) - assert.Equal(t, s1, s11) - - s2 := &v1.Schema{ - Name: "plugins/p2", - } - s3 := &v1.Schema{ - Name: "plugins/p3", - } - assert.Nil(t, c.InsertSchema(s2), "inserting schema s2") - assert.Nil(t, c.InsertSchema(s3), "inserting schema s3") - - s22, err := c.GetSchema("plugins/p2") - assert.Nil(t, err) - assert.Equal(t, s2, s22) - - assert.Nil(t, c.DeleteSchema(s3), "delete schema s3") - - schemaList, err := c.ListSchema() - assert.Nil(t, err, "listing schema") - - if schemaList[0].Name > schemaList[1].Name { - schemaList[0], schemaList[1] = schemaList[1], schemaList[0] - } - assert.Equal(t, s1, schemaList[0]) - assert.Equal(t, s2, schemaList[1]) - - s4 := &v1.Schema{ - Name: "plugins/p4", - } - assert.Error(t, ErrNotFound, c.DeleteSchema(s4)) -} - -func TestMemDBCachePluginConfig(t *testing.T) { - c, err := NewMemDBCache() - assert.Nil(t, err, "NewMemDBCache") - - pc1 := &v1.PluginConfig{ - Metadata: v1.Metadata{ - ID: "1", - Name: "name1", - }, - } - assert.Nil(t, c.InsertPluginConfig(pc1), "inserting plugin_config pc1") - - pc11, err := c.GetPluginConfig("1") - assert.Nil(t, err) - assert.Equal(t, pc1, pc11) - - pc2 := &v1.PluginConfig{ - Metadata: v1.Metadata{ - ID: "2", - Name: "name2", - }, - } - pc3 := &v1.PluginConfig{ - Metadata: v1.Metadata{ - ID: "3", - Name: "name3", - }, - } - assert.Nil(t, c.InsertPluginConfig(pc2), "inserting plugin_config pc2") - assert.Nil(t, c.InsertPluginConfig(pc3), "inserting plugin_config pc3") - - pc22, err := c.GetPluginConfig("2") - assert.Nil(t, err) - assert.Equal(t, pc2, pc22) - - assert.Nil(t, c.DeletePluginConfig(pc3), "delete plugin_config pc3") - - pcList, err := c.ListPluginConfigs() - assert.Nil(t, err, "listing plugin_config") - - if pcList[0].Name > pcList[1].Name { - pcList[0], pcList[1] = pcList[1], pcList[0] - } - assert.Equal(t, pcList[0], pc1) - assert.Equal(t, pcList[1], pc2) - - pc4 := &v1.PluginConfig{ - Metadata: v1.Metadata{ - ID: "4", - Name: "name4", - }, - } - assert.Error(t, ErrNotFound, c.DeletePluginConfig(pc4)) -} diff --git a/pkg/dashboard/cache/noop_db.go b/pkg/dashboard/cache/noop_db.go deleted file mode 100644 index b0bbb7dad..000000000 --- a/pkg/dashboard/cache/noop_db.go +++ /dev/null @@ -1,161 +0,0 @@ -// 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 cache - -import ( - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -type noopCache struct { -} - -// NewMemDBCache creates a Cache object backs with a memory DB. -func NewNoopDBCache() (Cache, error) { - return &noopCache{}, nil -} - -func (c *noopCache) InsertRoute(r *v1.Route) error { - return nil -} - -func (c *noopCache) InsertSSL(ssl *v1.Ssl) error { - return nil -} - -func (c *noopCache) InsertService(u *v1.Service) error { - return nil -} - -func (c *noopCache) InsertStreamRoute(sr *v1.StreamRoute) error { - return nil -} - -func (c *noopCache) InsertGlobalRule(gr *v1.GlobalRule) error { - return nil -} - -func (c *noopCache) InsertConsumer(consumer *v1.Consumer) error { - return nil -} - -func (c *noopCache) InsertSchema(schema *v1.Schema) error { - return nil -} - -func (c *noopCache) InsertPluginConfig(pc *v1.PluginConfig) error { - return nil -} - -func (c *noopCache) GetRoute(id string) (*v1.Route, error) { - return nil, nil -} - -func (c *noopCache) GetSSL(id string) (*v1.Ssl, error) { - return nil, nil -} - -func (c *noopCache) GetService(id string) (*v1.Service, error) { - return nil, nil -} - -func (c *noopCache) GetStreamRoute(id string) (*v1.StreamRoute, error) { - return nil, nil -} - -func (c *noopCache) GetGlobalRule(id string) (*v1.GlobalRule, error) { - return nil, nil -} - -func (c *noopCache) GetConsumer(username string) (*v1.Consumer, error) { - return nil, nil -} - -func (c *noopCache) GetSchema(name string) (*v1.Schema, error) { - return nil, nil -} - -func (c *noopCache) GetPluginConfig(name string) (*v1.PluginConfig, error) { - return nil, nil -} - -func (c *noopCache) ListRoutes(...any) ([]*v1.Route, error) { - return nil, nil -} - -func (c *noopCache) ListSSL(...any) ([]*v1.Ssl, error) { - return nil, nil -} - -func (c *noopCache) ListServices(...any) ([]*v1.Service, error) { - return nil, nil -} - -func (c *noopCache) ListStreamRoutes() ([]*v1.StreamRoute, error) { - return nil, nil -} - -func (c *noopCache) ListGlobalRules() ([]*v1.GlobalRule, error) { - return nil, nil -} - -func (c *noopCache) ListConsumers() ([]*v1.Consumer, error) { - return nil, nil -} - -func (c *noopCache) ListSchema() ([]*v1.Schema, error) { - return nil, nil -} - -func (c *noopCache) ListPluginConfigs() ([]*v1.PluginConfig, error) { - return nil, nil -} - -func (c *noopCache) DeleteRoute(r *v1.Route) error { - return nil -} - -func (c *noopCache) DeleteSSL(ssl *v1.Ssl) error { - return nil -} - -func (c *noopCache) DeleteService(u *v1.Service) error { - return nil -} - -func (c *noopCache) DeleteStreamRoute(sr *v1.StreamRoute) error { - return nil -} - -func (c *noopCache) DeleteGlobalRule(gr *v1.GlobalRule) error { - return nil -} - -func (c *noopCache) DeleteConsumer(consumer *v1.Consumer) error { - return nil -} - -func (c *noopCache) DeleteSchema(schema *v1.Schema) error { - return nil -} - -func (c *noopCache) DeletePluginConfig(pc *v1.PluginConfig) error { - return nil -} - -func (c *noopCache) CheckServiceReference(u *v1.Service) error { - return nil -} - -func (c *noopCache) CheckPluginConfigReference(pc *v1.PluginConfig) error { - return nil -} diff --git a/pkg/dashboard/cache/schema.go b/pkg/dashboard/cache/schema.go deleted file mode 100644 index 0d8eb75a3..000000000 --- a/pkg/dashboard/cache/schema.go +++ /dev/null @@ -1,224 +0,0 @@ -// 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 cache - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-memdb" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -var ( - _schema = &memdb.DBSchema{ - Tables: map[string]*memdb.TableSchema{ - "route": { - Name: "route", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - "name": { - Name: "name", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "Name"}, - AllowMissing: true, - }, - "service_id": { - Name: "service_id", - Unique: false, - Indexer: &memdb.StringFieldIndex{Field: "ServiceID"}, - AllowMissing: true, - }, - "plugin_config_id": { - Name: "plugin_config_id", - Unique: false, - Indexer: &memdb.StringFieldIndex{Field: "PluginConfigId"}, - AllowMissing: true, - }, - "label": { - Name: "label", - Unique: false, - AllowMissing: true, - Indexer: &LabelIndexer{ - LabelKeys: []string{"kind", "namespace", "name"}, - GetLabels: func(obj any) map[string]string { - service, ok := obj.(*v1.Route) - if !ok { - return nil - } - return service.Labels - }, - }, - }, - }, - }, - "service": { - Name: "service", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - "name": { - Name: "name", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "Name"}, - AllowMissing: true, - }, - "label": { - Name: "label", - Unique: false, - AllowMissing: true, - Indexer: &LabelIndexer{ - LabelKeys: []string{"kind", "namespace", "name"}, - GetLabels: func(obj any) map[string]string { - service, ok := obj.(*v1.Service) - if !ok { - return nil - } - return service.Labels - }, - }, - }, - }, - }, - "ssl": { - Name: "ssl", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "stream_route": { - Name: "stream_route", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - "service_id": { - Name: "service_id", - Unique: false, - Indexer: &memdb.StringFieldIndex{Field: "ServiceID"}, - AllowMissing: true, - }, - }, - }, - "global_rule": { - Name: "global_rule", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "consumer": { - Name: "consumer", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "Username"}, - }, - }, - }, - "schema": { - Name: "schema", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "Name"}, - }, - }, - }, - "plugin_config": { - Name: "plugin_config", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - "name": { - Name: "name", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "Name"}, - AllowMissing: true, - }, - }, - }, - "upstream_service": { - Name: "upstream_service", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ServiceName"}, - }, - }, - }, - }, - } -) - -// LabelIndexer is a custom indexer for exact match indexing -type LabelIndexer struct { - LabelKeys []string - GetLabels func(any) map[string]string -} - -func (emi *LabelIndexer) FromObject(obj any) (bool, []byte, error) { - labels := emi.GetLabels(obj) - var labelValues []string - for _, key := range emi.LabelKeys { - if value, exists := labels[key]; exists { - labelValues = append(labelValues, value) - } - } - - if len(labelValues) == 0 { - return false, nil, nil - } - - return true, []byte(strings.Join(labelValues, "/")), nil -} - -func (emi *LabelIndexer) FromArgs(args ...any) ([]byte, error) { - if len(args) != len(emi.LabelKeys) { - return nil, fmt.Errorf("expected %d arguments, got %d", len(emi.LabelKeys), len(args)) - } - - labelValues := make([]string, 0, len(args)) - for _, arg := range args { - value, ok := arg.(string) - if !ok { - return nil, fmt.Errorf("argument is not a string") - } - labelValues = append(labelValues, value) - } - - return []byte(strings.Join(labelValues, "/")), nil -} diff --git a/pkg/dashboard/cluster.go b/pkg/dashboard/cluster.go deleted file mode 100644 index 50dea4ab2..000000000 --- a/pkg/dashboard/cluster.go +++ /dev/null @@ -1,995 +0,0 @@ -// 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 dashboard - -import ( - "bytes" - "context" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/url" - "strings" - "sync/atomic" - "time" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/multierr" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/util/wait" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" -) - -const ( - _defaultTimeout = 5 * time.Second - _defaultSyncInterval = 6 * time.Hour - - _cacheSyncing = iota - _cacheSynced -) - -var ( - // ErrClusterNotExist means a cluster doesn't exist. - ErrClusterNotExist = errors.New("cluster not exist") - // ErrDuplicatedCluster means the cluster adding request was - // rejected since the cluster was already created. - ErrDuplicatedCluster = errors.New("duplicated cluster") - // ErrFunctionDisabled means the APISIX function is disabled - ErrFunctionDisabled = errors.New("function disabled") - - DefaultLabelsManagedBy map[string]string = map[string]string{ - "managed-by": "apisix-ingress-controller", - } - - // ErrRouteNotFound means the [route, ssl, upstream] was not found. - ErrNotFound = cache.ErrNotFound - - errReadOnClosedResBody = errors.New("http: read on closed response body") -) - -// ClusterOptions contains parameters to customize APISIX client. -type ClusterOptions struct { - ControllerName string - AdminAPIVersion string - Name string - AdminKey string - BaseURL string - Timeout time.Duration - // SyncInterval is the interval to sync schema. - SyncComparison bool - EnableEtcdServer bool - Prefix string - ListenAddress string - SchemaSynced bool - SyncCache bool - SSLKeyEncryptSalt string - SkipTLSVerify bool - Labels map[string]string -} - -type cluster struct { - labels map[string]string - controllerName string - adminVersion string - name string - baseURL string - baseURLHost string - adminKey string - prefix string - cli *http.Client - cacheState int32 - cache cache.Cache - cacheSynced chan struct{} - cacheSyncErr error - route Route - service Service - ssl SSL - streamRoute StreamRoute - globalRules GlobalRule - consumer Consumer - plugin Plugin - schema Schema - pluginConfig PluginConfig - pluginMetadata PluginMetadata - waitforCacheSync bool - validator APISIXSchemaValidator - sslKeyEncryptSalt string -} - -func newCluster(ctx context.Context, o *ClusterOptions) (Cluster, error) { - if o.BaseURL == "" { - return nil, errors.New("empty base url") - } - if o.Timeout == time.Duration(0) { - o.Timeout = _defaultTimeout - } - o.BaseURL = strings.TrimSuffix(o.BaseURL, "/") - - u, err := url.Parse(o.BaseURL) - if err != nil { - return nil, err - } - - switch u.Scheme { - case "http": - if u.Port() == "" { - u.Host = u.Host + ":80" - } - case "https": - if u.Port() == "" { - u.Host = u.Host + ":443" - } - } - - // if the version is not v3, then fallback to v2 - adminVersion := o.AdminAPIVersion - c := &cluster{ - labels: o.Labels, - controllerName: o.ControllerName, - adminVersion: adminVersion, - name: o.Name, - baseURL: o.BaseURL, - baseURLHost: u.Host, - adminKey: o.AdminKey, - prefix: o.Prefix, - cli: &http.Client{ - Timeout: o.Timeout, - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 3 * time.Second, - }).Dial, - DialContext: (&net.Dialer{ - Timeout: 3 * time.Second, - }).DialContext, - ResponseHeaderTimeout: 30 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - TLSClientConfig: &tls.Config{InsecureSkipVerify: o.SkipTLSVerify}, - }, - }, - cacheState: _cacheSyncing, // default state - cacheSynced: make(chan struct{}), - sslKeyEncryptSalt: o.SSLKeyEncryptSalt, - } - - c.route = newRouteClient(c) - c.service = newServiceClient(c) - c.ssl = newSSLClient(c) - c.streamRoute = newStreamRouteClient(c) - c.globalRules = newGlobalRuleClient(c) - c.consumer = newConsumerClient(c) - c.plugin = newPluginClient(c) - c.schema = newSchemaClient(c) - c.pluginConfig = newPluginConfigClient(c) - c.pluginMetadata = newPluginMetadataClient(c) - c.validator = newDummyValidator() - - c.cache, err = cache.NewMemDBCache() - if err != nil { - return nil, err - } - if o.SyncCache { - c.waitforCacheSync = true - go c.syncCache(ctx) - } - - return c, nil -} - -func (c *cluster) syncCache(ctx context.Context) { - log.Infow("syncing cache", zap.String("cluster", c.name)) - now := time.Now() - defer func() { - if c.cacheSyncErr == nil { - log.Infow("cache synced", - zap.String("cost_time", time.Since(now).String()), - zap.String("cluster", c.name), - ) - } else { - log.Errorw("failed to sync cache", - zap.String("cost_time", time.Since(now).String()), - zap.String("cluster", c.name), - ) - } - }() - - backoff := wait.Backoff{ - Duration: 2 * time.Second, - Factor: 1, - Steps: 5, - } - var lastSyncErr error - err := wait.ExponentialBackoff(backoff, func() (done bool, err error) { - // impossibly return: false, nil - // so can safe used - done, lastSyncErr = c.syncCacheOnce(ctx) - select { - case <-ctx.Done(): - err = context.Canceled - default: - break - } - return - }) - if err != nil { - // if ErrWaitTimeout then set lastSyncErr - c.cacheSyncErr = lastSyncErr - } - close(c.cacheSynced) - - if !atomic.CompareAndSwapInt32(&c.cacheState, _cacheSyncing, _cacheSynced) { - panic("dubious state when sync cache") - } -} - -func (c *cluster) syncCacheOnce(ctx context.Context) (bool, error) { - routes, err := c.route.List(ctx) - if err != nil { - log.Errorf("failed to list routes in APISIX: %s", err) - return false, err - } - ssl, err := c.ssl.List(ctx) - if err != nil { - log.Errorf("failed to list ssl in APISIX: %s", err) - return false, err - } - globalRules, err := c.globalRules.List(ctx) - if err != nil { - log.Errorf("failed to list global_rules in APISIX: %s", err) - return false, err - } - consumers, err := c.consumer.List(ctx) - if err != nil { - log.Errorf("failed to list consumers in APISIX: %s", err) - return false, err - } - - for _, r := range routes { - log.Debug("syncing route with labels", r.Labels) - if err := c.cache.InsertRoute(r); err != nil { - log.Errorw("failed to insert route to cache", - zap.String("route", r.ID), - zap.String("cluster", c.name), - zap.String("error", err.Error()), - ) - return false, err - } - } - for _, s := range ssl { - log.Debug("syncing ssl with labels", s.Labels) - if err := c.cache.InsertSSL(s); err != nil { - log.Errorw("failed to insert ssl to cache", - zap.String("ssl", s.ID), - zap.String("cluster", c.name), - zap.String("error", err.Error()), - ) - return false, err - } - } - for _, gr := range globalRules { - if err := c.cache.InsertGlobalRule(gr); err != nil { - log.Errorw("failed to insert global_rule to cache", - zap.Any("global_rule", gr), - zap.String("cluster", c.name), - zap.String("error", err.Error()), - ) - return false, err - } - } - for _, consumer := range consumers { - log.Debug("syncing consumer with labels", consumer.Labels) - if err := c.cache.InsertConsumer(consumer); err != nil { - log.Errorw("failed to insert consumer to cache", - zap.Any("consumer", consumer), - zap.String("cluster", c.name), - zap.String("error", err.Error()), - ) - } - } - log.Info("All cache synced successfully") - // for _, u := range pluginConfigs { - // if err := c.cache.InsertPluginConfig(u); err != nil { - // log.Errorw("failed to insert pluginConfig to cache", - // zap.String("pluginConfig", u.ID), - // zap.String("cluster", c.name), - // zap.String("error", err.Error()), - // ) - // return false, err - // } - // } - return true, nil -} - -// String implements Cluster.String method. -func (c *cluster) String() string { - return fmt.Sprintf("name=%s; base_url=%s", c.name, c.baseURL) -} - -// HasSynced implements Cluster.HasSynced method. -func (c *cluster) HasSynced(ctx context.Context) error { - if !c.waitforCacheSync { - return nil - } - if c.cacheSyncErr != nil { - return c.cacheSyncErr - } - if atomic.LoadInt32(&c.cacheState) == _cacheSynced { - return nil - } - - // still in sync - now := time.Now() - log.Warnf("waiting cluster %s to ready, it may takes a while", c.name) - select { - case <-ctx.Done(): - log.Errorf("failed to wait cluster to ready: %s", ctx.Err()) - return ctx.Err() - case <-c.cacheSynced: - if c.cacheSyncErr != nil { - // See https://github.com/apache/apisix-ingress-controller/issues/448 - // for more details. - return c.cacheSyncErr - } - log.Warnf("cluster %s now is ready, cost time %s", c.name, time.Since(now).String()) - return nil - } -} - -// Route implements Cluster.Route method. -func (c *cluster) Route() Route { - return c.route -} - -// Upstream implements Cluster.Upstream method. -func (c *cluster) Service() Service { - return c.service -} - -// SSL implements Cluster.SSL method. -func (c *cluster) SSL() SSL { - return c.ssl -} - -// StreamRoute implements Cluster.StreamRoute method. -func (c *cluster) StreamRoute() StreamRoute { - return c.streamRoute -} - -// GlobalRule implements Cluster.GlobalRule method. -func (c *cluster) GlobalRule() GlobalRule { - return c.globalRules -} - -// Consumer implements Cluster.Consumer method. -func (c *cluster) Consumer() Consumer { - return c.consumer -} - -// Plugin implements Cluster.Plugin method. -func (c *cluster) Plugin() Plugin { - return c.plugin -} - -// PluginConfig implements Cluster.PluginConfig method. -func (c *cluster) PluginConfig() PluginConfig { - return c.pluginConfig -} - -// Schema implements Cluster.Schema method. -func (c *cluster) Schema() Schema { - return c.schema -} - -func (c *cluster) PluginMetadata() PluginMetadata { - return c.pluginMetadata -} - -func (c *cluster) Validator() APISIXSchemaValidator { - return c.validator -} - -// HealthCheck implements Cluster.HealthCheck method. -// -// It checks the health of an APISIX cluster by performing a TCP socket probe -// against the baseURLHost. It will retry up to 3 times with exponential backoff -// before returning an error. -// -// Parameters: -// -// ctx: The context for the health check. -// -// Returns: -// -// err: Any error encountered while performing the health check. -func (c *cluster) HealthCheck(ctx context.Context) (err error) { - // Retry three times in a row, and exit if all of them fail. - backoff := wait.Backoff{ - Duration: 5 * time.Second, - Factor: 1, - Steps: 3, - } - - err = wait.ExponentialBackoffWithContext(ctx, backoff, func(ctx context.Context) (done bool, _ error) { - if lastCheckErr := c.healthCheck(ctx); lastCheckErr != nil { - log.Warnf("failed to check health for cluster %s: %s, will retry", c.name, lastCheckErr) - return - } - done = true - return - }) - - return err -} - -func (c *cluster) healthCheck(ctx context.Context) (err error) { - // tcp socket probe - d := net.Dialer{Timeout: 3 * time.Second} - conn, err := d.DialContext(ctx, "tcp", c.baseURLHost) - if err != nil { - return err - } - defer func(conn net.Conn) { - err := conn.Close() - if err != nil { - log.Warnw("failed to close tcp probe connection", - zap.Error(err), - zap.String("cluster", c.name), - ) - } - }(conn) - - return -} - -func (c *cluster) applyAuth(req *http.Request) { - if c.adminKey != "" { - req.Header.Set("X-API-Key", c.adminKey) - } -} - -func (c *cluster) do(req *http.Request) (*http.Response, error) { - c.applyAuth(req) - return c.cli.Do(req) -} - -func (c *cluster) isFunctionDisabled(body string) bool { - return strings.Contains(body, "is disabled") -} - -func (c *cluster) getResource(ctx context.Context, url, resource string) (*getResponse, error) { - log.Debugw("get resource in cluster", - zap.String("cluster_name", c.name), - zap.String("name", resource), - zap.String("url", url), - ) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, err - } - resp, err := c.do(req) - if err != nil { - return nil, err - } - - defer drainBody(resp.Body, url) - if resp.StatusCode != http.StatusOK { - body := readBody(resp.Body, url) - if c.isFunctionDisabled(body) { - return nil, ErrFunctionDisabled - } - if resp.StatusCode == http.StatusNotFound { - return nil, cache.ErrNotFound - } else { - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", body)) - } - return nil, err - } - - if c.adminVersion == "v3" { - var res getResponse - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&res); err != nil { - return nil, err - } - return &res, nil - } - var res getResponse - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&res); err != nil { - return nil, err - } - return &res, nil -} - -func addQueryParam(urlStr string, labels map[string]string) string { - parsedUrl, err := url.Parse(urlStr) - if err != nil { - return urlStr - } - query := parsedUrl.Query() - for key, value := range labels { - query.Add(fmt.Sprintf("labels[%s]", key), value) - } - parsedUrl.RawQuery = query.Encode() - return parsedUrl.String() -} - -func (c *cluster) listResource(ctx context.Context, url, resource string) (listResponse, error) { - var list listResponse - err := c.listResourceToResponse(ctx, url, resource, &list) - return list, err -} - -func (c *cluster) listResourceToResponse(ctx context.Context, url, resource string, listResponse any) error { - log.Debugw("list resource in cluster", - zap.String("cluster_name", c.name), - zap.String("name", resource), - zap.String("url", url), - ) - if c.labels != nil { - url = addQueryParam(url, c.labels) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return err - } - resp, err := c.do(req) - if err != nil { - return err - } - - defer drainBody(resp.Body, url) - if resp.StatusCode != http.StatusOK { - body := readBody(resp.Body, url) - if c.isFunctionDisabled(body) { - return ErrFunctionDisabled - } - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", body)) - return err - } - - return json.NewDecoder(resp.Body).Decode(listResponse) -} - -func (c *cluster) createResource(ctx context.Context, url, resource string, body []byte) (*getResponse, error) { - log.Debugw("creating resource in cluster", - zap.String("cluster_name", c.name), - zap.String("name", resource), - zap.String("url", url), - zap.ByteString("body", body), - ) - req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - resp, err := c.do(req) - if err != nil { - return nil, err - } - defer drainBody(resp.Body, url) - if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { - body := readBody(resp.Body, url) - if c.isFunctionDisabled(body) { - return nil, ErrFunctionDisabled - } - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", body)) - return nil, err - } - - var cr getResponse - byt, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if err := json.Unmarshal(byt, &cr); err != nil { - return nil, err - } - return &cr, nil -} - -func (c *cluster) updateResource(ctx context.Context, url, resource string, body []byte) (*getResponse, error) { - log.Debugw("updating resource in cluster", - zap.String("cluster_name", c.name), - zap.String("name", resource), - zap.String("url", url), - zap.ByteString("body", body), - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - resp, err := c.do(req) - if err != nil { - return nil, err - } - - defer drainBody(resp.Body, url) - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { - body := readBody(resp.Body, url) - log.Debugw("update response", - zap.Int("status code %d", resp.StatusCode), - zap.String("body %s", body), - ) - if c.isFunctionDisabled(body) { - return nil, ErrFunctionDisabled - } - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", body)) - return nil, err - } - if c.adminVersion == "v3" { - var ur updateResponseV3 - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&ur); err != nil { - return nil, err - } - - return &ur, nil - } - var ur updateResponse - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&ur); err != nil { - return nil, err - } - return &ur, nil -} - -func (c *cluster) deleteResource(ctx context.Context, url, resource string) error { - log.Debugw("deleting resource in cluster", - zap.String("cluster_name", c.name), - zap.String("name", resource), - zap.String("url", url), - ) - req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) - if err != nil { - return err - } - resp, err := c.do(req) - if err != nil { - return err - } - - defer drainBody(resp.Body, url) - - if resp.StatusCode != http.StatusOK && - resp.StatusCode != http.StatusNoContent && - resp.StatusCode != http.StatusNotFound { - message := readBody(resp.Body, url) - if c.isFunctionDisabled(message) { - return ErrFunctionDisabled - } - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", message)) - if strings.Contains(message, "still using") { - return cache.ErrStillInUse - } - return err - } - return nil -} - -// drainBody reads whole data until EOF from r, then close it. -func drainBody(r io.ReadCloser, url string) { - _, err := io.Copy(io.Discard, r) - if err != nil { - if err.Error() != errReadOnClosedResBody.Error() { - log.Warnw("failed to drain body (read)", - zap.String("url", url), - zap.Error(err), - ) - } - } - - if err := r.Close(); err != nil { - log.Warnw("failed to drain body (close)", - zap.String("url", url), - zap.Error(err), - ) - } -} - -func readBody(r io.ReadCloser, url string) string { - defer func() { - if err := r.Close(); err != nil { - log.Warnw("failed to close body", zap.String("url", url), zap.Error(err)) - } - }() - data, err := io.ReadAll(r) - if err != nil { - log.Warnw("failed to read body", zap.String("url", url), zap.Error(err)) - return "" - } - return string(data) -} - -// getSchema returns the schema of APISIX object. -func (c *cluster) getSchema(_ context.Context, url, resource string) (string, error) { - log.Debugw("get schema in cluster", - zap.String("url", url), - zap.String("cluster", c.name), - zap.String("resource", resource), - ) - // TODO: fixme The above passed context gets cancelled for some reason. Investigate - req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, nil) - if err != nil { - return "", err - } - resp, err := c.do(req) - if err != nil { - return "", err - } - - defer drainBody(resp.Body, url) - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNotFound { - return "", cache.ErrNotFound - } else { - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", readBody(resp.Body, url))) - } - return "", err - } - - return readBody(resp.Body, url), nil -} - -// getList returns a list of string. -func (c *cluster) getList(ctx context.Context, url, resource string) ([]string, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, err - } - resp, err := c.do(req) - if err != nil { - return nil, err - } - - defer drainBody(resp.Body, url) - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNotFound { - return nil, cache.ErrNotFound - } else { - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", readBody(resp.Body, url))) - } - return nil, err - } - - // In EE, for plugins the response is an array of string and not an object. - // sent to /list - if resource == "plugin" { - byt, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - var listResponse []string - err = json.Unmarshal(byt, &listResponse) - if err != nil { - return nil, err - } - return listResponse, nil - } - var listResponse map[string]any - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&listResponse); err != nil { - return nil, err - } - res := make([]string, 0, len(listResponse)) - - for name := range listResponse { - res = append(res, name) - } - return res, nil -} - -func (c *cluster) GetGlobalRule(ctx context.Context, baseUrl, id string) (*v1.GlobalRule, error) { - url := baseUrl + "/" + id - resp, err := c.getResource(ctx, url, "globalRule") - if err != nil { - return nil, err - } - - globalRule, err := resp.globalRule() - if err != nil { - log.Errorw("failed to convert global_rule item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - - return globalRule, nil -} - -func (c *cluster) GetConsumer(ctx context.Context, baseUrl, name string) (*v1.Consumer, error) { - url := baseUrl + "/" + name - resp, err := c.getResource(ctx, url, "consumer") - if err != nil { - return nil, err - } - - consumer, err := resp.consumer() - if err != nil { - log.Errorw("failed to convert consumer item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - return consumer, nil -} - -func (c *cluster) GetPluginConfig(ctx context.Context, baseUrl, id string) (*v1.PluginConfig, error) { - url := baseUrl + "/" + id - resp, err := c.getResource(ctx, url, "pluginConfig") - if err != nil { - return nil, err - } - - pluginConfig, err := resp.pluginConfig() - if err != nil { - log.Errorw("failed to convert pluginConfig item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - return pluginConfig, nil -} - -func (c *cluster) GetRoute(ctx context.Context, baseUrl, id string) (*v1.Route, error) { - url := baseUrl + "/" + id - resp, err := c.getResource(ctx, url, "route") - if err != nil { - return nil, err - } - - route, err := resp.route() - if err != nil { - log.Errorw("failed to convert route item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - return route, nil -} - -func (c *cluster) GetStreamRoute(ctx context.Context, baseUrl, id string) (*v1.StreamRoute, error) { - url := baseUrl + "/" + id - resp, err := c.getResource(ctx, url, "streamRoute") - if err != nil { - return nil, err - } - - streamRoute, err := resp.streamRoute() - if err != nil { - log.Errorw("failed to convert stream_route item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - return streamRoute, nil -} - -func (c *cluster) GetService(ctx context.Context, baseUrl, id string) (*v1.Service, error) { - url := baseUrl + "/" + id - resp, err := c.getResource(ctx, url, "service") - if err != nil { - return nil, err - } - svc, err := resp.service() - if err != nil { - log.Errorw("failed to convert service item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - return svc, nil -} - -func (c *cluster) GetSSL(ctx context.Context, baseUrl, id string) (*v1.Ssl, error) { - url := baseUrl + "/" + id - resp, err := c.getResource(ctx, url, "ssl") - if err != nil { - return nil, err - } - ssl, err := resp.ssl() - if err != nil { - return nil, err - } - return ssl, nil -} - -func getFromCacheOrAPI[T any]( - ctx context.Context, - id string, - url string, - cacheGet func(string) (T, error), - cacheInsert func(T) error, - apiGet func(context.Context, string, string) (T, error), -) (T, error) { - item, err := cacheGet(id) - if err == nil { - return item, nil - } - if err != cache.ErrNotFound { - log.Errorw("failed to find in cache, will try to lookup from APISIX", - zap.Error(err), - ) - } else { - log.Debugw("not found in cache, will try to lookup from APISIX", - zap.Error(err), - ) - } - - // TODO Add mutex here to avoid dog-pile effect. - item, err = apiGet(ctx, url, id) - if err != nil { - return item, err - } - - if err := cacheInsert(item); err != nil { - log.Errorf("failed to reflect create to cache: %s", err) - return item, err - } - return item, nil -} - -func updateResource[T any]( - ctx context.Context, - obj T, - url string, - resourceType string, - apiUpdate func(context.Context, string, string, []byte) (*getResponse, error), - cacheInsert func(T) error, - parseResponse func(*getResponse) (T, error), -) (T, error) { - var val T - body, err := json.Marshal(obj) - if err != nil { - return val, err - } - resp, err := apiUpdate(ctx, url, resourceType, body) - if err != nil { - return val, err - } - val, err = parseResponse(resp) - if err != nil { - return val, err - } - if err := cacheInsert(val); err != nil { - log.Errorf("failed to reflect update to cache: %s", err) - return val, err - } - return val, nil -} diff --git a/pkg/dashboard/consumer.go b/pkg/dashboard/consumer.go deleted file mode 100644 index 254cae2d9..000000000 --- a/pkg/dashboard/consumer.go +++ /dev/null @@ -1,151 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type consumerClient struct { - url string - cluster *cluster -} - -func newConsumerClient(c *cluster) Consumer { - return &consumerClient{ - url: c.baseURL + "/consumers", - cluster: c, - } -} - -// Get returns the Consumer. -// FIXME, currently if caller pass a non-existent resource, the Get always passes -// through cache. -func (r *consumerClient) Get(ctx context.Context, name string) (*v1.Consumer, error) { - return getFromCacheOrAPI( - ctx, - id.GenID(name), - r.url, - r.cluster.cache.GetConsumer, - r.cluster.cache.InsertConsumer, - r.cluster.GetConsumer, - ) -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (r *consumerClient) List(ctx context.Context) ([]*v1.Consumer, error) { - log.Debugw("try to list consumers in APISIX", - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - url := r.url - consumerItems, err := r.cluster.listResource(ctx, url, "consumer") - if err != nil { - log.Errorf("failed to list consumers: %s", err) - return nil, err - } - items := make([]*v1.Consumer, 0, len(consumerItems.List)) - for _, item := range consumerItems.List { - consumer, err := item.consumer() - if err != nil { - log.Errorw("failed to convert consumer item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - - items = append(items, consumer) - } - - return items, nil -} - -func (r *consumerClient) Create(ctx context.Context, obj *v1.Consumer) (*v1.Consumer, error) { - log.Debugw("try to create consumer", - zap.String("name", obj.Username), - zap.Any("plugins", obj.Plugins), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - - if err := r.cluster.HasSynced(ctx); err != nil { - return nil, err - } - data, err := json.Marshal(obj) - if err != nil { - return nil, err - } - - url := r.url + "/" + obj.Username - resp, err := r.cluster.createResource(ctx, url, "consumer", data) - if err != nil { - log.Errorf("failed to create consumer: %s", err) - return nil, err - } - consumer, err := resp.consumer() - if err != nil { - return nil, err - } - if err := r.cluster.cache.InsertConsumer(consumer); err != nil { - log.Errorf("failed to reflect consumer create to cache: %s", err) - return nil, err - } - return consumer, nil -} - -func (r *consumerClient) Delete(ctx context.Context, obj *v1.Consumer) error { - log.Debugw("try to delete consumer", - zap.String("name", obj.Username), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - if err := r.cluster.HasSynced(ctx); err != nil { - return err - } - url := r.url + "/" + obj.Username - if err := r.cluster.deleteResource(ctx, url, "consumer"); err != nil { - return err - } - if err := r.cluster.cache.DeleteConsumer(obj); err != nil { - log.Errorf("failed to reflect consumer delete to cache: %s", err) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (r *consumerClient) Update(ctx context.Context, obj *v1.Consumer) (*v1.Consumer, error) { - url := r.url + "/" + obj.Username - return updateResource( - ctx, - obj, - url, - "consumer", - r.cluster.updateResource, - r.cluster.cache.InsertConsumer, - func(resp *getResponse) (*v1.Consumer, error) { - return resp.consumer() - }, - ) -} diff --git a/pkg/dashboard/consumer_test.go b/pkg/dashboard/consumer_test.go deleted file mode 100644 index d068525ab..000000000 --- a/pkg/dashboard/consumer_test.go +++ /dev/null @@ -1,237 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "golang.org/x/net/nettest" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -type fakeAPISIXConsumerSrv struct { - consumer map[string]map[string]any -} - -type Value map[string]any - -type fakeListResp struct { - Total string `json:"total"` - List []fakeListItem `json:"list"` -} - -type fakeGetCreateResp struct { - fakeGetCreateItem -} - -type fakeGetCreateItem struct { - Value Value `json:"value"` - Key string `json:"key"` -} - -type fakeListItem Value - -func (srv *fakeAPISIXConsumerSrv) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer func() { - _ = r.Body.Close() - }() - - if !strings.HasPrefix(r.URL.Path, "/apisix/admin/consumers") { - w.WriteHeader(http.StatusNotFound) - return - } - - if r.Method == http.MethodGet { - // For individual resource, the getcreate response is sent - var key string - if strings.HasPrefix(r.URL.Path, "/apisix/admin/consumers/") && - strings.TrimPrefix(r.URL.Path, "/apisix/admin/consumers/") != "" { - key = strings.TrimPrefix(r.URL.Path, "/apisix/admin/consumers/") - } - if key != "" { - resp := fakeGetCreateResp{ - fakeGetCreateItem{ - Key: key, - Value: srv.consumer[key], - }, - } - resp.Value = srv.consumer[key] - w.WriteHeader(http.StatusOK) - data, _ := json.Marshal(resp) - _, _ = w.Write(data) - } else { - resp := fakeListResp{} - resp.Total = fmt.Sprintf("%d", len(srv.consumer)) - resp.List = make([]fakeListItem, 0, len(srv.consumer)) - for _, v := range srv.consumer { - resp.List = append(resp.List, v) - } - data, _ := json.Marshal(resp) - _, _ = w.Write(data) - } - - return - } - - if r.Method == http.MethodDelete { - id := strings.TrimPrefix(r.URL.Path, "/apisix/admin/consumers/") - id = "/apisix/admin/consumers/" + id - code := http.StatusNotFound - if _, ok := srv.consumer[id]; ok { - delete(srv.consumer, id) - code = http.StatusOK - } - w.WriteHeader(code) - } - - if r.Method == http.MethodPut { - paths := strings.Split(r.URL.Path, "/") - key := fmt.Sprintf("/apisix/admin/consumers/%s", paths[len(paths)-1]) - data, _ := io.ReadAll(r.Body) - w.WriteHeader(http.StatusCreated) - consumer := make(map[string]any, 0) - _ = json.Unmarshal(data, &consumer) - srv.consumer[key] = consumer - var val Value - _ = json.Unmarshal(data, &val) - resp := fakeGetCreateResp{ - fakeGetCreateItem{ - Value: val, - Key: key, - }, - } - data, _ = json.Marshal(resp) - _, _ = w.Write(data) - return - } - - if r.Method == http.MethodPatch { - id := strings.TrimPrefix(r.URL.Path, "/apisix/admin/consumers/") - id = "/apisix/admin/consumers/" + id - if _, ok := srv.consumer[id]; !ok { - w.WriteHeader(http.StatusNotFound) - return - } - - data, _ := io.ReadAll(r.Body) - var val Value - _ = json.Unmarshal(data, &val) - consumer := make(map[string]any, 0) - _ = json.Unmarshal(data, &consumer) - srv.consumer[id] = consumer - w.WriteHeader(http.StatusOK) - resp := fakeGetCreateResp{ - fakeGetCreateItem{ - Value: val, - Key: id, - }, - } - byt, _ := json.Marshal(resp) - _, _ = w.Write(byt) - return - } -} - -func runFakeConsumerSrv(t *testing.T) *http.Server { - srv := &fakeAPISIXConsumerSrv{ - consumer: make(map[string]map[string]any), - } - - ln, _ := nettest.NewLocalListener("tcp") - - httpSrv := &http.Server{ - Addr: ln.Addr().String(), - Handler: srv, - } - - go func() { - if err := httpSrv.Serve(ln); err != nil && err != http.ErrServerClosed { - t.Errorf("failed to run http server: %s", err) - } - }() - - return httpSrv -} - -func TestConsumerClient(t *testing.T) { - srv := runFakeConsumerSrv(t) - defer func() { - assert.Nil(t, srv.Shutdown(context.Background())) - }() - - u := url.URL{ - Scheme: "http", - Host: srv.Addr, - Path: "/apisix/admin", - } - - closedCh := make(chan struct{}) - close(closedCh) - cli := newConsumerClient(&cluster{ - baseURL: u.String(), - cli: http.DefaultClient, - cache: &dummyCache{}, - cacheSynced: closedCh, - }) - - // Create - obj, err := cli.Create(context.Background(), &v1.Consumer{ - Username: "1", - }) - assert.Nil(t, err) - assert.Equal(t, "1", obj.Username) - - obj, err = cli.Create(context.Background(), &v1.Consumer{ - Username: "2", - }) - assert.Nil(t, err) - assert.Equal(t, "2", obj.Username) - - // List - objs, err := cli.List(context.Background()) - assert.Nil(t, err) - assert.Len(t, objs, 2) - assert.ElementsMatch(t, []string{"1", "2"}, []string{objs[0].Username, objs[1].Username}) - - // Delete then List - if objs[0].Username != "1" { - objs[0], objs[1] = objs[1], objs[0] - } - assert.Nil(t, cli.Delete(context.Background(), objs[0])) - objs, err = cli.List(context.Background()) - assert.Nil(t, err) - assert.Len(t, objs, 1) - assert.Equal(t, "2", objs[0].Username) - - // Patch then List - _, err = cli.Update(context.Background(), &v1.Consumer{ - Username: "2", - Plugins: map[string]any{ - "prometheus": struct{}{}, - }, - }) - assert.Nil(t, err) - objs, err = cli.List(context.Background()) - assert.Nil(t, err) - assert.Len(t, objs, 1) - assert.Equal(t, "2", objs[0].Username) -} diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go deleted file mode 100644 index 7baa26268..000000000 --- a/pkg/dashboard/dashboard.go +++ /dev/null @@ -1,250 +0,0 @@ -// 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 dashboard - -import ( - "context" - "sync" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -type Dashboard interface { - // Cluster specifies the target cluster to talk. - Cluster(name string) Cluster - // AddCluster adds a new cluster. - AddCluster(context.Context, *ClusterOptions) error - // UpdateCluster updates an existing cluster. - UpdateCluster(context.Context, *ClusterOptions) error - // ListClusters lists all APISIX clusters. - ListClusters() []Cluster - // DeleteCluster deletes the target APISIX cluster by its name. - DeleteCluster(name string) -} - -// Cluster defines specific operations that can be applied in an APISIX -// cluster. -type Cluster interface { - // Route returns a Route interface that can operate Route resources. - Route() Route - - Service() Service - // SSL returns a SSL interface that can operate SSL resources. - SSL() SSL - // StreamRoute returns a StreamRoute interface that can operate StreamRoute resources. - StreamRoute() StreamRoute - // GlobalRule returns a GlobalRule interface that can operate GlobalRule resources. - GlobalRule() GlobalRule - // String exposes the client information in human-readable format. - String() string - // HasSynced checks whether all resources in APISIX cluster is synced to cache. - HasSynced(context.Context) error - // Consumer returns a Consumer interface that can operate Consumer resources. - Consumer() Consumer - // HealthCheck checks apisix cluster health in realtime. - HealthCheck(context.Context) error - // Plugin returns a Plugin interface that can operate Plugin resources. - Plugin() Plugin - // PluginConfig returns a PluginConfig interface that can operate PluginConfig resources. - PluginConfig() PluginConfig - // Schema returns a Schema interface that can fetch schema of APISIX objects. - Schema() Schema - - PluginMetadata() PluginMetadata - - Validator() APISIXSchemaValidator -} - -// Route is the specific client interface to take over the create, update, -// list and delete for APISIX Route resource. -type Route interface { - Get(ctx context.Context, name string) (*v1.Route, error) - List(ctx context.Context, args ...any) ([]*v1.Route, error) - Create(ctx context.Context, route *v1.Route) (*v1.Route, error) - Delete(ctx context.Context, route *v1.Route) error - Update(ctx context.Context, route *v1.Route) (*v1.Route, error) -} - -// SSL is the specific client interface to take over the create, update, -// list and delete for APISIX SSL resource. -type SSL interface { - // name is namespace_sslname - Get(ctx context.Context, name string) (*v1.Ssl, error) - List(ctx context.Context, args ...any) ([]*v1.Ssl, error) - Create(ctx context.Context, ssl *v1.Ssl) (*v1.Ssl, error) - Delete(ctx context.Context, ssl *v1.Ssl) error - Update(ctx context.Context, ssl *v1.Ssl) (*v1.Ssl, error) -} - -// Upstream is the specific client interface to take over the create, update, -// list and delete for APISIX Upstream resource. -type Service interface { - Get(ctx context.Context, name string) (*v1.Service, error) - List(ctx context.Context, args ...any) ([]*v1.Service, error) - Create(ctx context.Context, svc *v1.Service) (*v1.Service, error) - Delete(ctx context.Context, svc *v1.Service) error - Update(ctx context.Context, svc *v1.Service) (*v1.Service, error) -} - -// StreamRoute is the specific client interface to take over the create, update, -// list and delete for APISIX Stream Route resource. -type StreamRoute interface { - Get(ctx context.Context, name string) (*v1.StreamRoute, error) - List(ctx context.Context) ([]*v1.StreamRoute, error) - Create(ctx context.Context, route *v1.StreamRoute) (*v1.StreamRoute, error) - Delete(ctx context.Context, route *v1.StreamRoute) error - Update(ctx context.Context, route *v1.StreamRoute) (*v1.StreamRoute, error) -} - -// GlobalRule is the specific client interface to take over the create, update, -// list and delete for APISIX Global Rule resource. -type GlobalRule interface { - Get(ctx context.Context, id string) (*v1.GlobalRule, error) - List(ctx context.Context) ([]*v1.GlobalRule, error) - Create(ctx context.Context, rule *v1.GlobalRule) (*v1.GlobalRule, error) - Delete(ctx context.Context, rule *v1.GlobalRule) error - Update(ctx context.Context, rule *v1.GlobalRule) (*v1.GlobalRule, error) -} - -// Consumer is the specific client interface to take over the create, update, -// list and delete for APISIX Consumer resource. -type Consumer interface { - Get(ctx context.Context, name string) (*v1.Consumer, error) - List(ctx context.Context) ([]*v1.Consumer, error) - Create(ctx context.Context, consumer *v1.Consumer) (*v1.Consumer, error) - Delete(ctx context.Context, consumer *v1.Consumer) error - Update(ctx context.Context, consumer *v1.Consumer) (*v1.Consumer, error) -} - -// Plugin is the specific client interface to fetch APISIX Plugin resource. -type Plugin interface { - List(ctx context.Context) ([]string, error) -} - -// Schema is the specific client interface to fetch the schema of APISIX objects. -type Schema interface { - GetPluginSchema(ctx context.Context, pluginName string) (*v1.Schema, error) - GetRouteSchema(ctx context.Context) (*v1.Schema, error) - GetUpstreamSchema(ctx context.Context) (*v1.Schema, error) - GetConsumerSchema(ctx context.Context) (*v1.Schema, error) - GetSslSchema(ctx context.Context) (*v1.Schema, error) - GetPluginConfigSchema(ctx context.Context) (*v1.Schema, error) -} - -// PluginConfig is the specific client interface to take over the create, update, -// list and delete for APISIX PluginConfig resource. -type PluginConfig interface { - Get(ctx context.Context, name string) (*v1.PluginConfig, error) - List(ctx context.Context) ([]*v1.PluginConfig, error) - Create(ctx context.Context, plugin *v1.PluginConfig) (*v1.PluginConfig, error) - Delete(ctx context.Context, plugin *v1.PluginConfig) error - Update(ctx context.Context, plugin *v1.PluginConfig) (*v1.PluginConfig, error) -} - -type PluginMetadata interface { - Get(ctx context.Context, name string) (*v1.PluginMetadata, error) - List(ctx context.Context) ([]*v1.PluginMetadata, error) - Delete(ctx context.Context, metadata *v1.PluginMetadata) error - Update(ctx context.Context, metadata *v1.PluginMetadata) (*v1.PluginMetadata, error) - Create(ctx context.Context, metadata *v1.PluginMetadata) (*v1.PluginMetadata, error) -} - -type APISIXSchemaValidator interface { - ValidateStreamPluginSchema(plugins v1.Plugins) (bool, error) - ValidateHTTPPluginSchema(plugins v1.Plugins) (bool, error) -} - -type apisix struct { - adminVersion string - mu sync.RWMutex - nonExistentCluster Cluster - clusters map[string]Cluster -} - -// NewClient creates an api7ee Dashboard client to perform resources change pushing. -func NewClient() (Dashboard, error) { - cli := &apisix{ - nonExistentCluster: newNonExistentCluster(), - clusters: make(map[string]Cluster), - } - return cli, nil -} - -// Cluster implements APISIX.Cluster method. -func (c *apisix) Cluster(name string) Cluster { - c.mu.RLock() - defer c.mu.RUnlock() - cluster, ok := c.clusters[name] - if !ok { - return c.nonExistentCluster - } - return cluster -} - -// ListClusters implements APISIX.ListClusters method. -func (c *apisix) ListClusters() []Cluster { - c.mu.RLock() - defer c.mu.RUnlock() - clusters := make([]Cluster, 0, len(c.clusters)) - for _, cluster := range c.clusters { - clusters = append(clusters, cluster) - } - return clusters -} - -// AddCluster implements APISIX.AddCluster method. -func (c *apisix) AddCluster(ctx context.Context, co *ClusterOptions) error { - c.mu.Lock() - defer c.mu.Unlock() - _, ok := c.clusters[co.Name] - if ok { - return ErrDuplicatedCluster - } - if co.AdminAPIVersion == "" { - co.AdminAPIVersion = c.adminVersion - } - cluster, err := newCluster(ctx, co) - if err != nil { - return err - } - c.clusters[co.Name] = cluster - return nil -} - -func (c *apisix) UpdateCluster(ctx context.Context, co *ClusterOptions) error { - c.mu.Lock() - defer c.mu.Unlock() - if _, ok := c.clusters[co.Name]; !ok { - return ErrClusterNotExist - } - - if co.AdminAPIVersion == "" { - co.AdminAPIVersion = c.adminVersion - } - cluster, err := newCluster(ctx, co) - if err != nil { - return err - } - - c.clusters[co.Name] = cluster - return nil -} - -func (c *apisix) DeleteCluster(name string) { - c.mu.Lock() - defer c.mu.Unlock() - - // Don't have to close or free some resources in that cluster, so - // just delete its index. - delete(c.clusters, name) -} diff --git a/pkg/dashboard/global_rule.go b/pkg/dashboard/global_rule.go deleted file mode 100644 index 04d58550d..000000000 --- a/pkg/dashboard/global_rule.go +++ /dev/null @@ -1,165 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" -) - -type globalRuleClient struct { - url string - cluster *cluster -} - -func newGlobalRuleClient(c *cluster) GlobalRule { - return &globalRuleClient{ - url: c.baseURL + "/global_rules", - cluster: c, - } -} - -// Get returns the GlobalRule. -// FIXME, currently if caller pass a non-existent resource, the Get always passes -// through cache. -func (r *globalRuleClient) Get(ctx context.Context, id string) (*v1.GlobalRule, error) { - return getFromCacheOrAPI( - ctx, - id, - r.url, - r.cluster.cache.GetGlobalRule, - r.cluster.cache.InsertGlobalRule, - r.cluster.GetGlobalRule, - ) -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (r *globalRuleClient) List(ctx context.Context) ([]*v1.GlobalRule, error) { - log.Debugw("try to list global_rules in APISIX", - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - url := r.url - globalRuleItems, err := r.cluster.listResource(ctx, url, "globalRule") - if err != nil { - log.Errorf("failed to list global_rules: %s", err) - return nil, err - } - - items := make([]*v1.GlobalRule, 0, len(globalRuleItems.List)) - for _, item := range globalRuleItems.List { - globalRule, err := item.globalRule() - if err != nil { - log.Errorw("failed to convert global_rule item", - zap.String("url", r.url), - zap.Error(err), - ) - return nil, err - } - - items = append(items, globalRule) - } - - return items, nil -} - -func (r *globalRuleClient) Create(ctx context.Context, obj *v1.GlobalRule) (*v1.GlobalRule, error) { - // Overwrite global rule ID with the plugin name - if len(obj.Plugins) == 0 { // This case will not happen as its handled at schema validation level - return nil, fmt.Errorf("global rule must have at least one plugin") - } - - // This is checked on dashboard that global rule id should be the plugin name - for pluginName := range obj.Plugins { - obj.ID = pluginName - break - } - - log.Debugw("try to create global_rule", - zap.String("id", obj.ID), - zap.Any("plugins", obj.Plugins), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - - if err := r.cluster.HasSynced(ctx); err != nil { - return nil, err - } - data, err := json.Marshal(obj) - if err != nil { - return nil, err - } - - url := r.url + "/" + obj.ID - log.Debugw("creating global_rule", zap.ByteString("body", data), zap.String("url", url)) - resp, err := r.cluster.createResource(ctx, url, "globalRule", data) - if err != nil { - log.Errorf("failed to create global_rule: %s", err) - return nil, err - } - - globalRules, err := resp.globalRule() - if err != nil { - return nil, err - } - if err := r.cluster.cache.InsertGlobalRule(globalRules); err != nil { - log.Errorf("failed to reflect global_rules create to cache: %s", err) - return nil, err - } - return globalRules, nil -} - -func (r *globalRuleClient) Delete(ctx context.Context, obj *v1.GlobalRule) error { - log.Debugw("try to delete global_rule", - zap.String("id", obj.ID), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - if err := r.cluster.HasSynced(ctx); err != nil { - return err - } - url := r.url + "/" + obj.ID - if err := r.cluster.deleteResource(ctx, url, "globalRule"); err != nil { - return err - } - if err := r.cluster.cache.DeleteGlobalRule(obj); err != nil { - log.Errorf("failed to reflect global_rule delete to cache: %s", err) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (r *globalRuleClient) Update(ctx context.Context, obj *v1.GlobalRule) (*v1.GlobalRule, error) { - url := r.url + "/" + obj.ID - return updateResource( - ctx, - obj, - url, - "globalRule", - r.cluster.updateResource, - r.cluster.cache.InsertGlobalRule, - func(gr *getResponse) (*v1.GlobalRule, error) { - return gr.globalRule() - }, - ) -} diff --git a/pkg/dashboard/nonexistentclient.go b/pkg/dashboard/nonexistentclient.go deleted file mode 100644 index 002be24a2..000000000 --- a/pkg/dashboard/nonexistentclient.go +++ /dev/null @@ -1,373 +0,0 @@ -// 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 dashboard - -import ( - "context" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" -) - -type nonExistentCluster struct { - embedDummyResourceImplementer -} - -func newNonExistentCluster() *nonExistentCluster { - return &nonExistentCluster{ - embedDummyResourceImplementer{ - route: &dummyRoute{}, - ssl: &dummySSL{}, - service: &dummyService{}, - streamRoute: &dummyStreamRoute{}, - globalRule: &dummyGlobalRule{}, - consumer: &dummyConsumer{}, - plugin: &dummyPlugin{}, - schema: &dummySchema{}, - pluginConfig: &dummyPluginConfig{}, - pluginMetadata: &dummyPluginMetadata{}, - }, - } -} - -type embedDummyResourceImplementer struct { - route Route - ssl SSL - service Service - streamRoute StreamRoute - globalRule GlobalRule - consumer Consumer - plugin Plugin - schema Schema - pluginConfig PluginConfig - pluginMetadata PluginMetadata - validator APISIXSchemaValidator -} - -type dummyRoute struct{} - -func (f *dummyRoute) Get(_ context.Context, _ string) (*v1.Route, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyRoute) List(_ context.Context, _ ...any) ([]*v1.Route, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyRoute) Create(_ context.Context, _ *v1.Route) (*v1.Route, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyRoute) Delete(_ context.Context, _ *v1.Route) error { - return ErrClusterNotExist -} - -func (f *dummyRoute) Update(_ context.Context, _ *v1.Route) (*v1.Route, error) { - return nil, ErrClusterNotExist -} - -type dummySSL struct{} - -func (f *dummySSL) Get(_ context.Context, _ string) (*v1.Ssl, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySSL) List(_ context.Context, _ ...any) ([]*v1.Ssl, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySSL) Create(_ context.Context, _ *v1.Ssl) (*v1.Ssl, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySSL) Delete(_ context.Context, _ *v1.Ssl) error { - return ErrClusterNotExist -} - -func (f *dummySSL) Update(_ context.Context, _ *v1.Ssl) (*v1.Ssl, error) { - return nil, ErrClusterNotExist -} - -type dummyService struct{} - -func (f *dummyService) Get(_ context.Context, _ string) (*v1.Service, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyService) List(_ context.Context, _ ...any) ([]*v1.Service, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyService) Create(_ context.Context, _ *v1.Service) (*v1.Service, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyService) Delete(_ context.Context, _ *v1.Service) error { - return ErrClusterNotExist -} - -func (f *dummyService) Update(_ context.Context, _ *v1.Service) (*v1.Service, error) { - return nil, ErrClusterNotExist -} - -type dummyStreamRoute struct{} - -func (f *dummyStreamRoute) Get(_ context.Context, _ string) (*v1.StreamRoute, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyStreamRoute) List(_ context.Context) ([]*v1.StreamRoute, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyStreamRoute) Create(_ context.Context, _ *v1.StreamRoute) (*v1.StreamRoute, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyStreamRoute) Delete(_ context.Context, _ *v1.StreamRoute) error { - return ErrClusterNotExist -} - -func (f *dummyStreamRoute) Update(_ context.Context, _ *v1.StreamRoute) (*v1.StreamRoute, error) { - return nil, ErrClusterNotExist -} - -type dummyGlobalRule struct{} - -func (f *dummyGlobalRule) Get(_ context.Context, _ string) (*v1.GlobalRule, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyGlobalRule) List(_ context.Context) ([]*v1.GlobalRule, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyGlobalRule) Create(_ context.Context, _ *v1.GlobalRule) (*v1.GlobalRule, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyGlobalRule) Delete(_ context.Context, _ *v1.GlobalRule) error { - return ErrClusterNotExist -} - -func (f *dummyGlobalRule) Update(_ context.Context, _ *v1.GlobalRule) (*v1.GlobalRule, error) { - return nil, ErrClusterNotExist -} - -type dummyConsumer struct{} - -func (f *dummyConsumer) Get(_ context.Context, _ string) (*v1.Consumer, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyConsumer) List(_ context.Context) ([]*v1.Consumer, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyConsumer) Create(_ context.Context, _ *v1.Consumer) (*v1.Consumer, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyConsumer) Delete(_ context.Context, _ *v1.Consumer) error { - return ErrClusterNotExist -} - -func (f *dummyConsumer) Update(_ context.Context, _ *v1.Consumer) (*v1.Consumer, error) { - return nil, ErrClusterNotExist -} - -type dummyPlugin struct{} - -func (f *dummyPlugin) List(_ context.Context) ([]string, error) { - return nil, ErrClusterNotExist -} - -type dummySchema struct{} - -func (f *dummySchema) GetPluginSchema(_ context.Context, _ string) (*v1.Schema, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySchema) GetRouteSchema(_ context.Context) (*v1.Schema, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySchema) GetUpstreamSchema(_ context.Context) (*v1.Schema, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySchema) GetConsumerSchema(_ context.Context) (*v1.Schema, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySchema) GetSslSchema(_ context.Context) (*v1.Schema, error) { - return nil, ErrClusterNotExist -} - -func (f *dummySchema) GetPluginConfigSchema(_ context.Context) (*v1.Schema, error) { - return nil, ErrClusterNotExist -} - -type dummyPluginConfig struct{} - -func (f *dummyPluginConfig) Get(_ context.Context, _ string) (*v1.PluginConfig, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyPluginConfig) List(_ context.Context) ([]*v1.PluginConfig, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyPluginConfig) Create(_ context.Context, _ *v1.PluginConfig) (*v1.PluginConfig, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyPluginConfig) Delete(_ context.Context, _ *v1.PluginConfig) error { - return ErrClusterNotExist -} - -func (f *dummyPluginConfig) Update(_ context.Context, _ *v1.PluginConfig) (*v1.PluginConfig, error) { - return nil, ErrClusterNotExist -} - -type dummyPluginMetadata struct { -} - -func (f *dummyPluginMetadata) Get(_ context.Context, _ string) (*v1.PluginMetadata, error) { - return nil, ErrClusterNotExist -} - -func (f *dummyPluginMetadata) List(_ context.Context) ([]*v1.PluginMetadata, error) { - return nil, ErrClusterNotExist -} -func (f *dummyPluginMetadata) Delete(_ context.Context, _ *v1.PluginMetadata) error { - return ErrClusterNotExist -} -func (f *dummyPluginMetadata) Update(_ context.Context, _ *v1.PluginMetadata) (*v1.PluginMetadata, error) { - return nil, ErrClusterNotExist -} -func (f *dummyPluginMetadata) Create(_ context.Context, _ *v1.PluginMetadata) (*v1.PluginMetadata, error) { - return nil, ErrClusterNotExist -} - -type dummyValidator struct{} - -func newDummyValidator() APISIXSchemaValidator { - return &dummyValidator{} -} - -func (d *dummyValidator) ValidateHTTPPluginSchema(plugins v1.Plugins) (bool, error) { - return true, nil -} - -func (d *dummyValidator) ValidateStreamPluginSchema(plugins v1.Plugins) (bool, error) { - return true, nil -} - -func (nc *nonExistentCluster) Route() Route { - return nc.route -} - -func (nc *nonExistentCluster) SSL() SSL { - return nc.ssl -} - -func (nc *nonExistentCluster) Service() Service { - return nc.service -} - -func (nc *nonExistentCluster) StreamRoute() StreamRoute { - return nc.streamRoute -} - -func (nc *nonExistentCluster) GlobalRule() GlobalRule { - return nc.globalRule -} - -func (nc *nonExistentCluster) Consumer() Consumer { - return nc.consumer -} - -func (nc *nonExistentCluster) Plugin() Plugin { - return nc.plugin -} - -func (nc *nonExistentCluster) Validator() APISIXSchemaValidator { - return nc.validator -} - -func (nc *nonExistentCluster) PluginConfig() PluginConfig { - return nc.pluginConfig -} - -func (nc *nonExistentCluster) Schema() Schema { - return nc.schema -} -func (nc *nonExistentCluster) PluginMetadata() PluginMetadata { - return nc.pluginMetadata -} - -func (nc *nonExistentCluster) HasSynced(_ context.Context) error { - return nil -} - -func (nc *nonExistentCluster) HealthCheck(_ context.Context) error { - return nil -} - -func (nc *nonExistentCluster) String() string { - return "non-existent cluster" -} - -type dummyCache struct{} - -var _ cache.Cache = &dummyCache{} - -func (c *dummyCache) InsertRoute(_ *v1.Route) error { return nil } -func (c *dummyCache) InsertSSL(_ *v1.Ssl) error { return nil } -func (c *dummyCache) InsertService(_ *v1.Service) error { return nil } -func (c *dummyCache) InsertStreamRoute(_ *v1.StreamRoute) error { return nil } -func (c *dummyCache) InsertGlobalRule(_ *v1.GlobalRule) error { return nil } -func (c *dummyCache) InsertConsumer(_ *v1.Consumer) error { return nil } -func (c *dummyCache) InsertSchema(_ *v1.Schema) error { return nil } -func (c *dummyCache) InsertPluginConfig(_ *v1.PluginConfig) error { return nil } -func (c *dummyCache) GetRoute(_ string) (*v1.Route, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetSSL(_ string) (*v1.Ssl, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetService(_ string) (*v1.Service, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetStreamRoute(_ string) (*v1.StreamRoute, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetGlobalRule(_ string) (*v1.GlobalRule, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetConsumer(_ string) (*v1.Consumer, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetSchema(_ string) (*v1.Schema, error) { return nil, cache.ErrNotFound } -func (c *dummyCache) GetPluginConfig(_ string) (*v1.PluginConfig, error) { - return nil, cache.ErrNotFound -} - -func (c *dummyCache) ListRoutes(...any) ([]*v1.Route, error) { return nil, nil } -func (c *dummyCache) ListSSL(_ ...any) ([]*v1.Ssl, error) { return nil, nil } -func (c *dummyCache) ListServices(...any) ([]*v1.Service, error) { return nil, nil } -func (c *dummyCache) ListStreamRoutes() ([]*v1.StreamRoute, error) { return nil, nil } -func (c *dummyCache) ListGlobalRules() ([]*v1.GlobalRule, error) { return nil, nil } -func (c *dummyCache) ListConsumers() ([]*v1.Consumer, error) { return nil, nil } -func (c *dummyCache) ListSchema() ([]*v1.Schema, error) { return nil, nil } -func (c *dummyCache) ListPluginConfigs() ([]*v1.PluginConfig, error) { return nil, nil } - -func (c *dummyCache) DeleteRoute(_ *v1.Route) error { return nil } -func (c *dummyCache) DeleteSSL(_ *v1.Ssl) error { return nil } -func (c *dummyCache) DeleteService(_ *v1.Service) error { return nil } -func (c *dummyCache) DeleteStreamRoute(_ *v1.StreamRoute) error { return nil } -func (c *dummyCache) DeleteGlobalRule(_ *v1.GlobalRule) error { return nil } -func (c *dummyCache) DeleteConsumer(_ *v1.Consumer) error { return nil } -func (c *dummyCache) DeleteSchema(_ *v1.Schema) error { return nil } -func (c *dummyCache) DeletePluginConfig(_ *v1.PluginConfig) error { return nil } -func (c *dummyCache) CheckServiceReference(_ *v1.Service) error { return nil } -func (c *dummyCache) CheckPluginConfigReference(_ *v1.PluginConfig) error { return nil } diff --git a/pkg/dashboard/noop.go b/pkg/dashboard/noop.go deleted file mode 100644 index be91ca895..000000000 --- a/pkg/dashboard/noop.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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 dashboard - -import ( - "context" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -var ( - _ StreamRoute = (*noopClient)(nil) -) - -type noopClient struct { -} - -func (r *noopClient) Get(ctx context.Context, name string) (*v1.StreamRoute, error) { - return nil, nil -} - -func (r *noopClient) List(ctx context.Context) ([]*v1.StreamRoute, error) { - return nil, nil -} - -func (r *noopClient) Create(ctx context.Context, obj *v1.StreamRoute) (*v1.StreamRoute, error) { - return nil, nil -} - -func (r *noopClient) Delete(ctx context.Context, obj *v1.StreamRoute) error { - return nil -} - -func (r *noopClient) Update(ctx context.Context, obj *v1.StreamRoute) (*v1.StreamRoute, error) { - return nil, nil -} diff --git a/pkg/dashboard/plugin.go b/pkg/dashboard/plugin.go deleted file mode 100644 index 67f2f77b9..000000000 --- a/pkg/dashboard/plugin.go +++ /dev/null @@ -1,48 +0,0 @@ -// 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 dashboard - -import ( - "context" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" -) - -type pluginClient struct { - url string - cluster *cluster -} - -func newPluginClient(c *cluster) Plugin { - return &pluginClient{ - url: c.baseURL + "/plugins", - cluster: c, - } -} - -// List returns the names of all plugins. -func (p *pluginClient) List(ctx context.Context) ([]string, error) { - log.Debugw("try to list plugin names in APISIX", - zap.String("cluster", p.cluster.name), - zap.String("url", p.url), - ) - url := p.url + "/list" - pluginList, err := p.cluster.getList(ctx, url, "plugin") - if err != nil { - log.Errorf("failed to list plugin names: %s", err) - return nil, err - } - log.Debugf("plugin list: %v", pluginList) - return pluginList, nil -} diff --git a/pkg/dashboard/plugin_metadata.go b/pkg/dashboard/plugin_metadata.go deleted file mode 100644 index 4124971f8..000000000 --- a/pkg/dashboard/plugin_metadata.go +++ /dev/null @@ -1,149 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -type pluginMetadataClient struct { - url string - cluster *cluster -} - -func newPluginMetadataClient(c *cluster) *pluginMetadataClient { - return &pluginMetadataClient{ - url: c.baseURL + "/plugin_metadata", - cluster: c, - } -} - -func (r *pluginMetadataClient) Get(ctx context.Context, name string) (*v1.PluginMetadata, error) { - log.Debugw("try to look up pluginMetadata", - zap.String("name", name), - zap.String("url", r.url), - zap.String("cluster", r.cluster.name), - ) - - // TODO Add mutex here to avoid dog-pile effect. - url := r.url + "/" + name - resp, err := r.cluster.getResource(ctx, url, "pluginMetadata") - if err != nil { - log.Errorw("failed to get pluginMetadata from APISIX", - zap.String("name", name), - zap.String("url", url), - zap.String("cluster", r.cluster.name), - zap.Error(err), - ) - return nil, err - } - - pluginMetadata, err := resp.pluginMetadata() - if err != nil { - log.Errorw("failed to convert pluginMetadata item", - zap.String("url", r.url), - zap.Error(err), - ) - return nil, err - } - return pluginMetadata, nil -} - -func (r *pluginMetadataClient) List(ctx context.Context) (list []*v1.PluginMetadata, err error) { - log.Debugw("try to list pluginMetadatas in APISIX", - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - var resp = struct { - Value map[string]map[string]any - }{} - err = r.cluster.listResourceToResponse(ctx, r.url, "plugin_metadata", &resp) - if err != nil { - log.Errorf("failed to list pluginMetadatas: %s", err) - return nil, err - } - for name, metadata := range resp.Value { - list = append(list, &v1.PluginMetadata{ - Name: name, - Metadata: metadata, - }) - } - - return -} - -func (r *pluginMetadataClient) Delete(ctx context.Context, obj *v1.PluginMetadata) error { - log.Debugw("try to delete pluginMetadata", - zap.String("name", obj.Name), - zap.Any("metadata", obj.Metadata), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - if err := r.cluster.HasSynced(ctx); err != nil { - return err - } - url := r.url + "/" + obj.Name - if err := r.cluster.deleteResource(ctx, url, "pluginMetadata"); err != nil { - return err - } - return nil -} - -func (r *pluginMetadataClient) Update(ctx context.Context, obj *v1.PluginMetadata) (*v1.PluginMetadata, error) { - url := r.url + "/" + obj.Name - return updateResource( - ctx, - obj, - url, - "pluginMetadata", - r.cluster.updateResource, - func(obj *v1.PluginMetadata) error { - return nil - }, - func(resp *getResponse) (*v1.PluginMetadata, error) { - return resp.pluginMetadata() - }, - ) -} - -func (r *pluginMetadataClient) Create(ctx context.Context, obj *v1.PluginMetadata) (*v1.PluginMetadata, error) { - log.Debugw("try to create pluginMetadata", - zap.String("name", obj.Name), - zap.Any("metadata", obj.Metadata), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - if err := r.cluster.HasSynced(ctx); err != nil { - return nil, err - } - body, err := json.Marshal(obj.Metadata) - if err != nil { - return nil, err - } - url := r.url + "/" + obj.Name - resp, err := r.cluster.updateResource(ctx, url, "pluginMetadata", body) - if err != nil { - return nil, err - } - pluginMetadata, err := resp.pluginMetadata() - if err != nil { - return nil, err - } - return pluginMetadata, nil -} diff --git a/pkg/dashboard/plugin_test.go b/pkg/dashboard/plugin_test.go deleted file mode 100644 index 7f6e0c3a4..000000000 --- a/pkg/dashboard/plugin_test.go +++ /dev/null @@ -1,116 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - "net/http" - "net/url" - "sort" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "golang.org/x/net/nettest" -) - -type fakeAPISIXPluginSrv struct { - plugins []string -} - -var fakePluginNames = []string{ - "plugin-1", - "plugin-2", - "plugin-3", -} - -func (srv *fakeAPISIXPluginSrv) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer func() { - _ = r.Body.Close() - }() - - if !strings.HasPrefix(r.URL.Path, "/apisix/admin/plugins") { - w.WriteHeader(http.StatusNotFound) - return - } - if strings.HasPrefix(r.URL.Path, "/apisix/admin/plugins/list") { - byt, _ := json.Marshal(fakePluginNames) - _, _ = w.Write(byt) - return - } - fakePluginsResp := make(map[string]any, len(srv.plugins)) - for _, fp := range srv.plugins { - fakePluginsResp[fp] = struct{}{} - } - - if r.Method == http.MethodGet { - data, _ := json.Marshal(fakePluginsResp) - _, _ = w.Write(data) - w.WriteHeader(http.StatusOK) - return - } -} - -func runFakePluginSrv(t *testing.T) *http.Server { - srv := &fakeAPISIXPluginSrv{ - plugins: fakePluginNames, - } - - ln, _ := nettest.NewLocalListener("tcp") - - httpSrv := &http.Server{ - Addr: ln.Addr().String(), - Handler: srv, - } - - go func() { - if err := httpSrv.Serve(ln); err != nil && err != http.ErrServerClosed { - t.Errorf("failed to run http server: %s", err) - } - }() - - return httpSrv -} - -func TestPluginClient(t *testing.T) { - srv := runFakePluginSrv(t) - defer func() { - assert.Nil(t, srv.Shutdown(context.Background())) - }() - - u := url.URL{ - Scheme: "http", - Host: srv.Addr, - Path: "/apisix/admin", - } - - closedCh := make(chan struct{}) - close(closedCh) - cli := newPluginClient(&cluster{ - baseURL: u.String(), - cli: http.DefaultClient, - cache: &dummyCache{}, - cacheSynced: closedCh, - }) - - // List - objs, err := cli.List(context.Background()) - assert.Nil(t, err) - assert.Len(t, objs, len(fakePluginNames)) - sort.Strings(fakePluginNames) - sort.Strings(objs) - for i := range fakePluginNames { - assert.Equal(t, fakePluginNames[i], objs[i]) - } -} diff --git a/pkg/dashboard/pluginconfig.go b/pkg/dashboard/pluginconfig.go deleted file mode 100644 index f3a8410e6..000000000 --- a/pkg/dashboard/pluginconfig.go +++ /dev/null @@ -1,159 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type pluginConfigClient struct { - url string - cluster *cluster -} - -func newPluginConfigClient(c *cluster) PluginConfig { - return &pluginConfigClient{ - url: c.baseURL + "/plugin_configs", - cluster: c, - } -} - -// Get returns the v1.PluginConfig. -// FIXME, currently if caller pass a non-existent resource, the Get always passes -// through cache. -func (pc *pluginConfigClient) Get(ctx context.Context, name string) (*v1.PluginConfig, error) { - return getFromCacheOrAPI( - ctx, - id.GenID(name), - pc.url, - pc.cluster.cache.GetPluginConfig, - pc.cluster.cache.InsertPluginConfig, - pc.cluster.GetPluginConfig, - ) -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (pc *pluginConfigClient) List(ctx context.Context) ([]*v1.PluginConfig, error) { - log.Debugw("try to list pluginConfig in APISIX", - zap.String("cluster", pc.cluster.name), - zap.String("url", pc.url), - ) - pluginConfigItems, err := pc.cluster.listResource(ctx, pc.url, "pluginConfig") - if err != nil { - log.Errorf("failed to list pluginConfig: %s", err) - return nil, err - } - - items := make([]*v1.PluginConfig, 0, len(pluginConfigItems.List)) - for _, item := range pluginConfigItems.List { - pluginConfig, err := item.pluginConfig() - if err != nil { - log.Errorw("failed to convert pluginConfig item", - zap.String("url", pc.url), - zap.Error(err), - ) - return nil, err - } - - items = append(items, pluginConfig) - } - - return items, nil -} - -func (pc *pluginConfigClient) Create(ctx context.Context, obj *v1.PluginConfig) (*v1.PluginConfig, error) { - log.Debugw("try to create pluginConfig", - zap.String("name", obj.Name), - zap.Any("plugins", obj.Plugins), - zap.String("cluster", pc.cluster.name), - zap.String("url", pc.url), - ) - - if err := pc.cluster.HasSynced(ctx); err != nil { - return nil, err - } - data, err := json.Marshal(obj) - if err != nil { - return nil, err - } - - url := pc.url + "/" + obj.ID - log.Debugw("creating pluginConfig", zap.ByteString("body", data), zap.String("url", url)) - resp, err := pc.cluster.createResource(ctx, url, "pluginConfig", data) - if err != nil { - log.Errorf("failed to create pluginConfig: %s", err) - return nil, err - } - - pluginConfig, err := resp.pluginConfig() - if err != nil { - return nil, err - } - if err := pc.cluster.cache.InsertPluginConfig(pluginConfig); err != nil { - log.Errorf("failed to reflect pluginConfig create to cache: %s", err) - return nil, err - } - return pluginConfig, nil -} - -func (pc *pluginConfigClient) Delete(ctx context.Context, obj *v1.PluginConfig) error { - log.Debugw("try to delete pluginConfig", - zap.String("id", obj.ID), - zap.String("name", obj.Name), - zap.String("cluster", pc.cluster.name), - zap.String("url", pc.url), - ) - err := pc.cluster.cache.CheckPluginConfigReference(obj) - if err != nil { - log.Warnw("deletion for plugin config: " + obj.Name + " aborted as it is still in use.") - return err - } - if err := pc.cluster.HasSynced(ctx); err != nil { - return err - } - url := pc.url + "/" + obj.ID - if err := pc.cluster.deleteResource(ctx, url, "pluginConfig"); err != nil { - return err - } - if err := pc.cluster.cache.DeletePluginConfig(obj); err != nil { - log.Errorf("failed to reflect pluginConfig delete to cache: %s", err) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (pc *pluginConfigClient) Update(ctx context.Context, obj *v1.PluginConfig) (*v1.PluginConfig, error) { - url := pc.url + "/" + obj.ID - return updateResource( - ctx, - obj, - url, - "pluginConfig", - pc.cluster.updateResource, - pc.cluster.cache.InsertPluginConfig, - func(resp *getResponse) (*v1.PluginConfig, error) { - return resp.pluginConfig() - }, - ) -} diff --git a/pkg/dashboard/resource.go b/pkg/dashboard/resource.go deleted file mode 100644 index 43eb0fa46..000000000 --- a/pkg/dashboard/resource.go +++ /dev/null @@ -1,381 +0,0 @@ -// 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 dashboard - -import ( - "encoding/json" - "strconv" - "strings" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -type getResponse struct { - Key string `json:"key"` - Value map[string]any `json:"value"` -} - -type listResponse struct { - Total IntOrString `json:"total"` - List listItems `json:"list"` -} - -type listItems []listItem - -type listItem map[string]any - -// IntOrString processing number and string types, after json deserialization will output int -type IntOrString struct { - IntValue int `json:"int_value"` -} - -func (ios *IntOrString) UnmarshalJSON(p []byte) error { - result := strings.Trim(string(p), "\"") - count, err := strconv.Atoi(result) - if err != nil { - return err - } - ios.IntValue = count - return nil -} - -type updateResponse = getResponse - -type updateResponseV3 = getResponse - -// type node struct { -// Key string `json:"key"` -// Items items `json:"nodes"` -// } - -// type items []item - -// // UnmarshalJSON implements json.Unmarshaler interface. -// // lua-cjson doesn't distinguish empty array and table, -// // and by default empty array will be encoded as '{}'. -// // We have to maintain the compatibility. -// func (items *items) UnmarshalJSON(p []byte) error { -// if p[0] == '{' { -// if len(p) != 2 { -// return errors.New("unexpected non-empty object") -// } -// return nil -// } -// var data []item -// if err := json.Unmarshal(p, &data); err != nil { -// return err -// } -// *items = data -// return nil -// } - -// type item struct { -// Key string `json:"key"` -// Value json.RawMessage `json:"value"` -// } - -// // route decodes item.Value and converts it to v1.Route. -// func (i *item) route() (*v1.Route, error) { -// log.Debugf("got route: %s", string(i.Value)) -// list := strings.Split(i.Key, "/") -// if len(list) < 1 { -// return nil, fmt.Errorf("bad route config key: %s", i.Key) -// } - -// var route v1.Route -// if err := json.Unmarshal(i.Value, &route); err != nil { -// return nil, err -// } -// return &route, nil -// } - -func (i *getResponse) route() (*v1.Route, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var route v1.Route - if err := json.Unmarshal(byt, &route); err != nil { - return nil, err - } - return &route, nil -} - -func (i *listItem) route() (*v1.Route, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var route v1.Route - if err := json.Unmarshal(byt, &route); err != nil { - return nil, err - } - return &route, nil -} - -func (i *listItem) streamRoute() (*v1.StreamRoute, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var streamRoute v1.StreamRoute - if err := json.Unmarshal(byt, &streamRoute); err != nil { - return nil, err - } - return &streamRoute, nil -} - -func (i *getResponse) streamRoute() (*v1.StreamRoute, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var streamRoute v1.StreamRoute - if err := json.Unmarshal(byt, &streamRoute); err != nil { - return nil, err - } - return &streamRoute, nil -} - -// upstream decodes item.Value and converts it to v1.Upstream. -// func (i *item) upstream() (*v1.Upstream, error) { -// log.Debugf("got upstream: %s", string(i.Value)) -// list := strings.Split(i.Key, "/") -// if len(list) < 1 { -// return nil, fmt.Errorf("bad upstream config key: %s", i.Key) -// } - -// var ups v1.Upstream -// if err := json.Unmarshal(i.Value, &ups); err != nil { -// return nil, err -// } - -// // This is a workaround scheme to avoid APISIX's -// // health check schema about the health checker intervals. -// if ups.Checks != nil && ups.Checks.Active != nil { -// if ups.Checks.Active.Healthy.Interval == 0 { -// ups.Checks.Active.Healthy.Interval = int(v1.ActiveHealthCheckMinInterval.Seconds()) -// } -// if ups.Checks.Active.Unhealthy.Interval == 0 { -// ups.Checks.Active.Healthy.Interval = int(v1.ActiveHealthCheckMinInterval.Seconds()) -// } -// } -// return &ups, nil -// } - -// upstream decodes response and converts it to v1.Upstream. -func (i *getResponse) service() (*v1.Service, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var svc v1.Service - if err := json.Unmarshal(byt, &svc); err != nil { - return nil, err - } - ups := svc.Upstream - ups.ID = svc.ID - // This is a workaround scheme to avoid APISIX's - // health check schema about the health checker intervals. - if ups.Checks != nil && ups.Checks.Active != nil { - if ups.Checks.Active.Healthy.Interval == 0 { - ups.Checks.Active.Healthy.Interval = int(v1.ActiveHealthCheckMinInterval.Seconds()) - } - if ups.Checks.Active.Unhealthy.Interval == 0 { - ups.Checks.Active.Healthy.Interval = int(v1.ActiveHealthCheckMinInterval.Seconds()) - } - } - svc.Upstream = ups - return &svc, nil -} - -func (i *listItem) service() (*v1.Service, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var svc v1.Service - if err := json.Unmarshal(byt, &svc); err != nil { - return nil, err - } - // This is a workaround scheme to avoid APISIX's - // health check schema about the health checker intervals. - if svc.Upstream.Checks != nil && svc.Upstream.Checks.Active != nil { - if svc.Upstream.Checks.Active.Healthy.Interval == 0 { - svc.Upstream.Checks.Active.Healthy.Interval = int(v1.ActiveHealthCheckMinInterval.Seconds()) - } - if svc.Upstream.Checks.Active.Unhealthy.Interval == 0 { - svc.Upstream.Checks.Active.Healthy.Interval = int(v1.ActiveHealthCheckMinInterval.Seconds()) - } - } - return &svc, nil -} - -// ssl decodes item.Value and converts it to v1.Ssl. -// func (i *item) ssl() (*v1.Ssl, error) { -// log.Debugf("got ssl: %s", string(i.Value)) -// var ssl v1.Ssl -// if err := json.Unmarshal(i.Value, &ssl); err != nil { -// return nil, err -// } -// return &ssl, nil -// } - -func (i *getResponse) ssl() (*v1.Ssl, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var ssl v1.Ssl - if err := json.Unmarshal(byt, &ssl); err != nil { - return nil, err - } - return &ssl, nil -} - -func (i *listItem) ssl() (*v1.Ssl, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var ssl v1.Ssl - if err := json.Unmarshal(byt, &ssl); err != nil { - return nil, err - } - return &ssl, nil -} - -// globalRule decodes item.Value and converts it to v1.GlobalRule. -// func (i *item) globalRule() (*v1.GlobalRule, error) { -// log.Debugf("got global_rule: %s", string(i.Value)) -// var globalRule v1.GlobalRule -// if err := json.Unmarshal(i.Value, &globalRule); err != nil { -// return nil, err -// } -// return &globalRule, nil -// } - -func (i *getResponse) globalRule() (*v1.GlobalRule, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var globalRule v1.GlobalRule - if err := json.Unmarshal(byt, &globalRule); err != nil { - return nil, err - } - return &globalRule, nil -} - -func (i *listItem) globalRule() (*v1.GlobalRule, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var globalRule v1.GlobalRule - if err := json.Unmarshal(byt, &globalRule); err != nil { - return nil, err - } - return &globalRule, nil -} - -// consumer decodes item.Value and converts it to v1.Consumer. -// func (i *item) consumer() (*v1.Consumer, error) { -// log.Debugf("got consumer: %s", string(i.Value)) -// var consumer v1.Consumer -// if err := json.Unmarshal(i.Value, &consumer); err != nil { -// return nil, err -// } -// return &consumer, nil -// } - -func (i *getResponse) consumer() (*v1.Consumer, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var consumer v1.Consumer - if err := json.Unmarshal(byt, &consumer); err != nil { - return nil, err - } - return &consumer, nil -} - -func (i *listItem) consumer() (*v1.Consumer, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var consumer v1.Consumer - if err := json.Unmarshal(byt, &consumer); err != nil { - return nil, err - } - return &consumer, nil -} - -// func (i *item) pluginMetadata() (*v1.PluginMetadata, error) { -// log.Debugf("got pluginMetadata: %s", string(i.Value)) -// var pluginMetadata v1.PluginMetadata -// if err := json.Unmarshal(i.Value, &pluginMetadata.Metadata); err != nil { -// return nil, err -// } -// keys := strings.Split(i.Key, "/") -// pluginMetadata.Name = keys[len(keys)-1] -// return &pluginMetadata, nil -// } - -func (i *getResponse) pluginMetadata() (*v1.PluginMetadata, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var pluginMetadata v1.PluginMetadata - if err := json.Unmarshal(byt, &pluginMetadata.Metadata); err != nil { - return nil, err - } - return &pluginMetadata, nil -} - -// // pluginConfig decodes item.Value and converts it to v1.PluginConfig. -// func (i *item) pluginConfig() (*v1.PluginConfig, error) { -// log.Debugf("got pluginConfig: %s", string(i.Value)) -// var pluginConfig v1.PluginConfig -// if err := json.Unmarshal(i.Value, &pluginConfig); err != nil { -// return nil, err -// } -// return &pluginConfig, nil -// } - -func (i *getResponse) pluginConfig() (*v1.PluginConfig, error) { - byt, err := json.Marshal(i.Value) - if err != nil { - return nil, err - } - var pluginConfig v1.PluginConfig - if err := json.Unmarshal(byt, &pluginConfig); err != nil { - return nil, err - } - return &pluginConfig, nil -} - -func (i *listItem) pluginConfig() (*v1.PluginConfig, error) { - byt, err := json.Marshal(i) - if err != nil { - return nil, err - } - var pluginConfig v1.PluginConfig - if err := json.Unmarshal(byt, &pluginConfig); err != nil { - return nil, err - } - return &pluginConfig, nil -} diff --git a/pkg/dashboard/route.go b/pkg/dashboard/route.go deleted file mode 100644 index cdb1cb8b0..000000000 --- a/pkg/dashboard/route.go +++ /dev/null @@ -1,154 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type routeClient struct { - url string - cluster *cluster -} - -func newRouteClient(c *cluster) Route { - return &routeClient{ - url: c.baseURL + "/routes", - cluster: c, - } -} - -// Get returns the Route. -// FIXME, currently if caller pass a non-existent resource, the Get always passes -// through cache. -func (r *routeClient) Get(ctx context.Context, name string) (*v1.Route, error) { - return getFromCacheOrAPI( - ctx, - id.GenID(name), - r.url, - r.cluster.cache.GetRoute, - r.cluster.cache.InsertRoute, - r.cluster.GetRoute, - ) -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (r *routeClient) List(ctx context.Context, args ...any) ([]*v1.Route, error) { - log.Debugw("try to list routes in APISIX", - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - routeItems, err := r.cluster.listResource(ctx, r.url, "route") - if err != nil { - log.Errorf("failed to list routes: %s", err) - return nil, err - } - - items := make([]*v1.Route, 0, len(routeItems.List)) - for _, item := range routeItems.List { - route, err := item.route() - if err != nil { - log.Errorw("failed to convert route item", - zap.String("url", r.url), - zap.Error(err), - ) - return nil, err - } - - items = append(items, route) - } - - return items, nil -} - -func (r *routeClient) Create(ctx context.Context, obj *v1.Route) (*v1.Route, error) { - obj.Name = obj.ID - log.Debugw("try to create route", - zap.Strings("hosts", obj.Hosts), - zap.String("name", obj.Name), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - - if err := r.cluster.HasSynced(ctx); err != nil { - return nil, err - } - data, err := json.Marshal(obj) - if err != nil { - return nil, err - } - - url := r.url + "/" + obj.ID - resp, err := r.cluster.createResource(ctx, url, "route", data) - if err != nil { - log.Errorf("failed to create route: %s", err) - return nil, err - } - - route, err := resp.route() - if err != nil { - return nil, err - } - if err := r.cluster.cache.InsertRoute(route); err != nil { - log.Errorf("failed to reflect route create to cache: %s", err) - return nil, err - } - return route, nil -} - -func (r *routeClient) Delete(ctx context.Context, obj *v1.Route) error { - log.Debugw("try to delete route", - zap.String("id", obj.ID), - zap.String("name", obj.Name), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - if err := r.cluster.HasSynced(ctx); err != nil { - return err - } - url := r.url + "/" + obj.ID - if err := r.cluster.deleteResource(ctx, url, "route"); err != nil { - return err - } - if err := r.cluster.cache.DeleteRoute(obj); err != nil { - log.Errorf("failed to reflect route delete to cache: %s", err) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (r *routeClient) Update(ctx context.Context, obj *v1.Route) (*v1.Route, error) { - url := r.url + "/" + obj.ID - return updateResource( - ctx, - obj, - url, - "route", - r.cluster.updateResource, - r.cluster.cache.InsertRoute, - func(resp *getResponse) (*v1.Route, error) { - return resp.route() - }, - ) -} diff --git a/pkg/dashboard/schema.go b/pkg/dashboard/schema.go deleted file mode 100644 index f8e85ecd5..000000000 --- a/pkg/dashboard/schema.go +++ /dev/null @@ -1,114 +0,0 @@ -// 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 dashboard - -import ( - "context" - "strings" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type schemaClient struct { - url string - cluster *cluster -} - -func newSchemaClient(c *cluster) Schema { - return &schemaClient{ - url: c.baseURL + "/schema", - cluster: c, - } -} - -// GetSchema returns APISIX object's schema. -func (sc schemaClient) getSchema(ctx context.Context, name string) (*v1.Schema, error) { - log.Debugw("try to look up schema", - zap.String("name", name), - zap.String("url", sc.url), - zap.String("cluster", sc.cluster.name), - ) - - sid := id.GenID(name) - schema, err := sc.cluster.cache.GetSchema(sid) - if err == nil { - return schema, nil - } - if err == cache.ErrNotFound { - log.Debugw("failed to find schema in cache, will try to lookup from APISIX", - zap.String("name", name), - zap.Error(err), - ) - } else { - log.Errorw("failed to find schema in cache, will try to lookup from APISIX", - zap.String("name", name), - zap.Error(err), - ) - } - // Dashboard uses /apisix/admin/plugins/{plugin_name} instead of /apisix/admin/schema/{plugin_name} to get schema - url := strings.Replace(sc.url, "schema", name, 1) - content, err := sc.cluster.getSchema(ctx, url, "schema") - if err != nil { - log.Errorw("failed to get schema from APISIX", - zap.String("name", name), - zap.String("url", url), - zap.String("cluster", sc.cluster.name), - zap.Error(err), - ) - return nil, err - } - schema = &v1.Schema{ - Name: name, - Content: content, - } - if err := sc.cluster.cache.InsertSchema(schema); err != nil { - log.Errorf("failed to reflect schema create to cache: %s", err) - return nil, err - } - return schema, nil -} - -// GetPluginSchema returns plugin's schema. -func (sc schemaClient) GetPluginSchema(ctx context.Context, pluginName string) (*v1.Schema, error) { - return sc.getSchema(ctx, "plugins/"+pluginName) -} - -// GetRouteSchema returns route's schema. -func (sc schemaClient) GetRouteSchema(ctx context.Context) (*v1.Schema, error) { - return sc.getSchema(ctx, "route") -} - -// GetUpstreamSchema returns upstream's schema. -func (sc schemaClient) GetUpstreamSchema(ctx context.Context) (*v1.Schema, error) { - return sc.getSchema(ctx, "upstream") -} - -// GetConsumerSchema returns consumer's schema. -func (sc schemaClient) GetConsumerSchema(ctx context.Context) (*v1.Schema, error) { - return sc.getSchema(ctx, "consumer") -} - -// GetSslSchema returns SSL's schema. -func (sc schemaClient) GetSslSchema(ctx context.Context) (*v1.Schema, error) { - return sc.getSchema(ctx, "ssl") -} - -// GetPluginConfigSchema returns PluginConfig's schema. -func (sc schemaClient) GetPluginConfigSchema(ctx context.Context) (*v1.Schema, error) { - return sc.getSchema(ctx, "pluginConfig") -} diff --git a/pkg/dashboard/service.go b/pkg/dashboard/service.go deleted file mode 100644 index 293ccc256..000000000 --- a/pkg/dashboard/service.go +++ /dev/null @@ -1,187 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type serviceClient struct { - url string - cluster *cluster -} - -func newServiceClient(c *cluster) Service { - return &serviceClient{ - url: c.baseURL + "/services", - cluster: c, - } -} - -func (u *serviceClient) Get(ctx context.Context, name string) (*v1.Service, error) { - return getFromCacheOrAPI( - ctx, - id.GenID(name), - u.url, - u.cluster.cache.GetService, - u.cluster.cache.InsertService, - u.cluster.GetService, - ) -} - -type ListFrom string - -var ( - ListFromCache ListFrom = "cache" - ListFromRemote ListFrom = "remote" -) - -type ListOptions struct { - From ListFrom - KindLabel ListByKindLabelOptions -} - -type ListByKindLabelOptions struct { - Kind string - Namespace string - Name string -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (u *serviceClient) List(ctx context.Context, listOptions ...any) ([]*v1.Service, error) { - var options ListOptions - if len(listOptions) > 0 { - options = listOptions[0].(ListOptions) - } - - if options.From == ListFromCache { - log.Debugw("try to list services in cache", - zap.String("cluster", u.cluster.name), - zap.String("url", u.url), - ) - return u.cluster.cache.ListServices("label", - options.KindLabel.Kind, - options.KindLabel.Namespace, - options.KindLabel.Name) - } - - log.Debugw("try to list upstreams in APISIX", - zap.String("url", u.url), - zap.String("cluster", u.cluster.name), - ) - upsItems, err := u.cluster.listResource(ctx, u.url, "service") - if err != nil { - log.Errorf("failed to list upstreams: %s", err) - return nil, err - } - - items := make([]*v1.Service, 0, len(upsItems.List)) - for _, item := range upsItems.List { - ups, err := item.service() - if err != nil { - log.Errorw("failed to convert upstream item", - zap.String("url", u.url), - zap.Error(err), - ) - return nil, err - } - items = append(items, ups) - } - return items, nil -} - -func (u *serviceClient) Create(ctx context.Context, obj *v1.Service) (*v1.Service, error) { - log.Debugw("try to create upstream", - zap.String("name", obj.Name), - zap.String("url", u.url), - zap.String("cluster", u.cluster.name), - ) - - if err := u.cluster.HasSynced(ctx); err != nil { - return nil, err - } - serviceObj := *obj - body, err := json.Marshal(serviceObj) - if err != nil { - return nil, err - } - url := u.url + "/" + obj.ID - log.Debugw("creating service", zap.ByteString("body", body), zap.String("url", url)) - resp, err := u.cluster.createResource(ctx, url, "service", body) - if err != nil { - log.Errorf("failed to create upstream: %s", err) - return nil, err - } - ups, err := resp.service() - if err != nil { - return nil, err - } - if err := u.cluster.cache.InsertService(ups); err != nil { - log.Errorf("failed to reflect upstream create to cache: %s", err) - return nil, err - } - return ups, err -} - -func (u *serviceClient) Delete(ctx context.Context, obj *v1.Service) error { - log.Debugw("try to delete upstream", - zap.String("id", obj.ID), - zap.String("name", obj.Name), - zap.String("cluster", u.cluster.name), - zap.String("url", u.url), - ) - err := u.cluster.cache.CheckServiceReference(obj) - if err != nil { - log.Warnw("deletion for upstream: " + obj.Name + " aborted as it is still in use.") - return err - } - if err := u.cluster.HasSynced(ctx); err != nil { - return err - } - url := u.url + "/" + obj.ID - if err := u.cluster.deleteResource(ctx, url, "service"); err != nil { - return err - } - if err := u.cluster.cache.DeleteService(obj); err != nil { - log.Errorf("failed to reflect upstream delete to cache: %s", err.Error()) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (u *serviceClient) Update(ctx context.Context, obj *v1.Service) (*v1.Service, error) { - url := u.url + "/" + obj.ID - log.Debugw("try to update service", zap.Any("service", obj), zap.String("url", url)) - return updateResource( - ctx, - obj, - url, - "service", - u.cluster.updateResource, - u.cluster.cache.InsertService, - func(resp *getResponse) (*v1.Service, error) { - return resp.service() - }, - ) -} diff --git a/pkg/dashboard/ssl.go b/pkg/dashboard/ssl.go deleted file mode 100644 index d6030fee5..000000000 --- a/pkg/dashboard/ssl.go +++ /dev/null @@ -1,165 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type sslClient struct { - url string - cluster *cluster -} - -func newSSLClient(c *cluster) SSL { - return &sslClient{ - url: c.baseURL + "/ssls", - cluster: c, - } -} - -// name is namespace_sslname -func (s *sslClient) Get(ctx context.Context, name string) (*v1.Ssl, error) { - return getFromCacheOrAPI( - ctx, - id.GenID(name), - s.url, - s.cluster.cache.GetSSL, - s.cluster.cache.InsertSSL, - s.cluster.GetSSL, - ) -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (s *sslClient) List(ctx context.Context, listOptions ...any) ([]*v1.Ssl, error) { - var options ListOptions - if len(listOptions) > 0 { - options = listOptions[0].(ListOptions) - } - if options.From == ListFromCache { - log.Debugw("try to list ssls in cache", - zap.String("cluster", s.cluster.name), - zap.String("url", s.url), - ) - return s.cluster.cache.ListSSL( - "label", - options.KindLabel.Kind, - options.KindLabel.Namespace, - options.KindLabel.Name, - ) - } - log.Debugw("try to list ssl in APISIX", - zap.String("url", s.url), - zap.String("cluster", s.cluster.name), - ) - url := s.url - sslItems, err := s.cluster.listResource(ctx, url, "ssls") - if err != nil { - log.Errorf("failed to list ssl: %s", err) - return nil, err - } - - items := make([]*v1.Ssl, 0, len(sslItems.List)) - for _, item := range sslItems.List { - ssl, err := item.ssl() - if err != nil { - log.Errorw("failed to convert ssl item", - zap.String("url", url), - zap.Error(err), - ) - return nil, err - } - - items = append(items, ssl) - } - - return items, nil -} - -func (s *sslClient) Create(ctx context.Context, obj *v1.Ssl) (*v1.Ssl, error) { - log.Debugw("try to create ssl", - zap.String("cluster", s.cluster.name), - zap.String("url", s.url), - zap.String("id", obj.ID), - ) - if err := s.cluster.HasSynced(ctx); err != nil { - return nil, err - } - data, err := json.Marshal(obj) - if err != nil { - return nil, err - } - url := s.url + "/" + obj.ID - log.Debugw("creating ssl", zap.ByteString("body", data), zap.String("url", url)) - resp, err := s.cluster.createResource(ctx, url, "ssls", data) - if err != nil { - log.Errorf("failed to create ssl: %s", err) - return nil, err - } - - ssl, err := resp.ssl() - if err != nil { - return nil, err - } - if err := s.cluster.cache.InsertSSL(ssl); err != nil { - log.Errorf("failed to reflect ssl create to cache: %s", err) - return nil, err - } - return ssl, nil -} - -func (s *sslClient) Delete(ctx context.Context, obj *v1.Ssl) error { - log.Debugw("try to delete ssl", - zap.String("id", obj.ID), - zap.String("cluster", s.cluster.name), - zap.String("url", s.url), - ) - if err := s.cluster.HasSynced(ctx); err != nil { - return err - } - url := s.url + "/" + obj.ID - if err := s.cluster.deleteResource(ctx, url, "ssls"); err != nil { - return err - } - if err := s.cluster.cache.DeleteSSL(obj); err != nil { - log.Errorf("failed to reflect ssl delete to cache: %s", err) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (s *sslClient) Update(ctx context.Context, obj *v1.Ssl) (*v1.Ssl, error) { - url := s.url + "/" + obj.ID - return updateResource( - ctx, - obj, - url, - "ssls", - s.cluster.updateResource, - s.cluster.cache.InsertSSL, - func(resp *getResponse) (*v1.Ssl, error) { - return resp.ssl() - }, - ) -} diff --git a/pkg/dashboard/stream_route.go b/pkg/dashboard/stream_route.go deleted file mode 100644 index b69623bfc..000000000 --- a/pkg/dashboard/stream_route.go +++ /dev/null @@ -1,159 +0,0 @@ -// 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 dashboard - -import ( - "context" - "encoding/json" - - "github.com/api7/gopkg/pkg/log" - "go.uber.org/zap" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/pkg/dashboard/cache" - "github.com/apache/apisix-ingress-controller/pkg/id" -) - -type streamRouteClient struct { - url string - cluster *cluster -} - -func newStreamRouteClient(c *cluster) StreamRoute { - url := c.baseURL + "/stream_routes" - _, err := c.listResource(context.Background(), url, "streamRoute") - if err == ErrFunctionDisabled { - log.Infow("resource stream_routes is disabled") - return &noopClient{} - } - return &streamRouteClient{ - url: url, - cluster: c, - } -} - -// Get returns the StreamRoute. -// FIXME, currently if caller pass a non-existent resource, the Get always passes -// through cache. -func (r *streamRouteClient) Get(ctx context.Context, name string) (*v1.StreamRoute, error) { - return getFromCacheOrAPI( - ctx, - id.GenID(name), - r.url, - r.cluster.cache.GetStreamRoute, - r.cluster.cache.InsertStreamRoute, - r.cluster.GetStreamRoute, - ) -} - -// List is only used in cache warming up. So here just pass through -// to APISIX. -func (r *streamRouteClient) List(ctx context.Context) ([]*v1.StreamRoute, error) { - log.Debugw("try to list stream_routes in APISIX", - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - streamRouteItems, err := r.cluster.listResource(ctx, r.url, "streamRoute") - if err != nil { - log.Errorf("failed to list stream_routes: %s", err) - return nil, err - } - - items := make([]*v1.StreamRoute, 0, len(streamRouteItems.List)) - for _, item := range streamRouteItems.List { - streamRoute, err := item.streamRoute() - if err != nil { - log.Errorw("failed to convert stream_route item", - zap.String("url", r.url), - zap.Error(err), - ) - return nil, err - } - - items = append(items, streamRoute) - } - return items, nil -} - -func (r *streamRouteClient) Create(ctx context.Context, obj *v1.StreamRoute) (*v1.StreamRoute, error) { - log.Debugw("try to create stream_route", - zap.String("id", obj.ID), - zap.Int32("server_port", obj.ServerPort), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - zap.String("sni", obj.SNI), - ) - - if err := r.cluster.HasSynced(ctx); err != nil { - return nil, err - } - data, err := json.Marshal(obj) - if err != nil { - return nil, err - } - - url := r.url + "/" + obj.ID - log.Infow("creating stream_route", zap.ByteString("body", data), zap.String("url", url)) - resp, err := r.cluster.createResource(ctx, url, "streamRoute", data) - if err != nil { - log.Errorf("failed to create stream_route: %s", err) - return nil, err - } - - streamRoute, err := resp.streamRoute() - if err != nil { - return nil, err - } - if err := r.cluster.cache.InsertStreamRoute(streamRoute); err != nil { - log.Errorf("failed to reflect stream_route create to cache: %s", err) - return nil, err - } - return streamRoute, nil -} - -func (r *streamRouteClient) Delete(ctx context.Context, obj *v1.StreamRoute) error { - log.Debugw("try to delete stream_route", - zap.String("id", obj.ID), - zap.String("cluster", r.cluster.name), - zap.String("url", r.url), - ) - if err := r.cluster.HasSynced(ctx); err != nil { - return err - } - url := r.url + "/" + obj.ID - if err := r.cluster.deleteResource(ctx, url, "streamRoute"); err != nil { - return err - } - if err := r.cluster.cache.DeleteStreamRoute(obj); err != nil { - log.Errorf("failed to reflect stream_route delete to cache: %s", err) - if err != cache.ErrNotFound { - return err - } - } - return nil -} - -func (r *streamRouteClient) Update(ctx context.Context, obj *v1.StreamRoute) (*v1.StreamRoute, error) { - url := r.url + "/" + obj.ID - return updateResource( - ctx, - obj, - url, - "streamRoute", - r.cluster.updateResource, - r.cluster.cache.InsertStreamRoute, - func(resp *getResponse) (*v1.StreamRoute, error) { - return resp.streamRoute() - }, - ) -} diff --git a/pkg/dashboard/utils.go b/pkg/dashboard/utils.go deleted file mode 100644 index 0c1605c76..000000000 --- a/pkg/dashboard/utils.go +++ /dev/null @@ -1,80 +0,0 @@ -// 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 dashboard - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "encoding/base64" - "errors" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -var ( - ErrUnknownApisixResourceType = errors.New("unknown apisix resource type") -) - -type ResourceTypes interface { - *v1.Route | *v1.Ssl | *v1.Service | *v1.StreamRoute | *v1.GlobalRule | *v1.Consumer | *v1.PluginConfig -} - -func PKCS5Padding(plaintext []byte, blockSize int) []byte { - padding := blockSize - len(plaintext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(plaintext, padtext...) -} - -func PKCS5UnPadding(origData []byte) []byte { - length := len(origData) - unpadding := int(origData[length-1]) - return origData[:(length - unpadding)] -} - -func AesEncrypt(origData, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - blockSize := block.BlockSize() - origData = PKCS5Padding(origData, blockSize) - blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) - crypted := make([]byte, len(origData)) - blockMode.CryptBlocks(crypted, origData) - return crypted, nil -} - -func AesDecrypt(crypted, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - blockSize := block.BlockSize() - blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) - origData := make([]byte, len(crypted)) - blockMode.CryptBlocks(origData, crypted) - origData = PKCS5UnPadding(origData) - return origData, nil -} - -func AesEencryptPrivatekey(data []byte, aeskey []byte) (string, error) { - xcode, err := AesEncrypt(data, aeskey) - if err != nil { - return "", err - } - - return base64.StdEncoding.EncodeToString(xcode), nil -} diff --git a/pkg/dashboard/validator.go b/pkg/dashboard/validator.go deleted file mode 100644 index cf1dbfc7a..000000000 --- a/pkg/dashboard/validator.go +++ /dev/null @@ -1,131 +0,0 @@ -// 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 dashboard - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/hashicorp/go-multierror" - "github.com/xeipuuv/gojsonschema" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -type APISIXSchema struct { - Plugins map[string]SchemaPlugin `json:"plugins"` - StreamPlugins map[string]SchemaPlugin `json:"stream_plugins"` -} - -type SchemaPlugin struct { - SchemaContent any `json:"schema"` -} - -type PluginSchemaDef map[string]gojsonschema.JSONLoader - -type apisixSchemaReferenceValidator struct { - StreamPlugins PluginSchemaDef - HTTPPlugins PluginSchemaDef -} - -func NewReferenceFile(source string) (APISIXSchemaValidator, error) { - data, err := os.ReadFile(source) - if err != nil { - return nil, fmt.Errorf("error reading file: %w", err) - } - - var schemadef APISIXSchema - err = json.Unmarshal(data, &schemadef) - if err != nil { - return nil, fmt.Errorf("error parsing JSON: %w", err) - } - - validator := &apisixSchemaReferenceValidator{ - HTTPPlugins: make(PluginSchemaDef), - StreamPlugins: make(PluginSchemaDef), - } - - for _, plugin := range []struct { - name string - schema map[string]SchemaPlugin - }{ - {name: "HTTPPlugins", schema: schemadef.Plugins}, - {name: "StreamPlugins", schema: schemadef.StreamPlugins}, - } { - for k, v := range plugin.schema { - switch plugin.name { - case "HTTPPlugins": - validator.HTTPPlugins[k] = gojsonschema.NewGoLoader(v.SchemaContent) - case "StreamPlugins": - validator.StreamPlugins[k] = gojsonschema.NewGoLoader(v.SchemaContent) - } - } - } - - return validator, nil -} - -func (asv *apisixSchemaReferenceValidator) ValidateHTTPPluginSchema(plugins v1.Plugins) (bool, error) { - var resultErrs error - - for pluginName, pluginConfig := range plugins { - schema, ok := asv.HTTPPlugins[pluginName] - if !ok { - return false, fmt.Errorf("unknown plugin [%s]", pluginName) - } - result, err := gojsonschema.Validate(schema, gojsonschema.NewGoLoader(pluginConfig)) - if err != nil { - return false, err - } - - if result.Valid() { - continue - } - - resultErrs = multierror.Append(resultErrs, fmt.Errorf("plugin [%s] config is invalid", pluginName)) - for _, desc := range result.Errors() { - resultErrs = multierror.Append(resultErrs, fmt.Errorf("- %s", desc)) - } - return false, resultErrs - } - - return true, nil -} - -func (asv *apisixSchemaReferenceValidator) ValidateStreamPluginSchema(plugins v1.Plugins) (bool, error) { - var resultErrs error - - for pluginName, pluginConfig := range plugins { - schema, ok := asv.StreamPlugins[pluginName] - if !ok { - return false, fmt.Errorf("unknown stream plugin [%s]", pluginName) - } - result, err := gojsonschema.Validate(schema, gojsonschema.NewGoLoader(pluginConfig)) - if err != nil { - return false, err - } - - if result.Valid() { - continue - } - - resultErrs = multierror.Append(resultErrs, fmt.Errorf("stream plugin [%s] config is invalid", pluginName)) - for _, desc := range result.Errors() { - resultErrs = multierror.Append(resultErrs, fmt.Errorf("- %s", desc)) - } - return false, resultErrs - } - - return true, nil -} diff --git a/test/conformance/conformance_test.go b/test/conformance/conformance_test.go deleted file mode 100644 index c664f1fed..000000000 --- a/test/conformance/conformance_test.go +++ /dev/null @@ -1,85 +0,0 @@ -//go:build conformance -// +build conformance - -package conformance - -import ( - "flag" - "os" - "testing" - - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/gateway-api/conformance" - conformancev1 "sigs.k8s.io/gateway-api/conformance/apis/v1" - "sigs.k8s.io/gateway-api/conformance/tests" - "sigs.k8s.io/gateway-api/conformance/utils/suite" - "sigs.k8s.io/gateway-api/pkg/features" - "sigs.k8s.io/yaml" -) - -var skippedTestsForSSL = []string{ - // Reason: https://github.com/kubernetes-sigs/gateway-api/blob/5c5fc388829d24e8071071b01e8313ada8f15d9f/conformance/utils/suite/suite.go#L358. SAN includes '*' - tests.HTTPRouteHTTPSListener.ShortName, - tests.HTTPRouteRedirectPortAndScheme.ShortName, -} - -var skippedTestsForTraditionalRoutes = []string{ - tests.HTTPRouteInvalidCrossNamespaceBackendRef.ShortName, - tests.HTTPRouteInvalidReferenceGrant.ShortName, - tests.HTTPRoutePartiallyInvalidViaInvalidReferenceGrant.ShortName, - tests.HTTPRouteReferenceGrant.ShortName, - - // TODO: HTTPRoute hostname intersection and listener hostname matching -} - -var gatewaySupportedFeatures = []features.FeatureName{ - features.SupportGateway, - features.SupportHTTPRoute, - // features.SupportHTTPRouteMethodMatching, - // features.SupportHTTPRouteResponseHeaderModification, - // features.SupportHTTPRouteRequestMirror, - // features.SupportHTTPRouteBackendRequestHeaderModification, - // features.SupportHTTPRouteHostRewrite, -} - -func TestGatewayAPIConformance(t *testing.T) { - flag.Parse() - - opts := conformance.DefaultOptions(t) - opts.Debug = true - opts.CleanupBaseResources = true - opts.GatewayClassName = gatewayClassName - opts.SupportedFeatures = sets.New(gatewaySupportedFeatures...) - opts.SkipTests = append(skippedTestsForSSL, skippedTestsForTraditionalRoutes...) - opts.Implementation = conformancev1.Implementation{ - Organization: "APISIX", - Project: "apisix-ingress-controller", - URL: "https://github.com/apache/apisix-ingress-controller.git", - Version: "v2.0.0", - } - opts.ConformanceProfiles = sets.New(suite.GatewayHTTPConformanceProfileName) - - cSuite, err := suite.NewConformanceTestSuite(opts) - require.NoError(t, err) - - t.Log("starting the gateway conformance test suite") - cSuite.Setup(t, tests.ConformanceTests) - - if err := cSuite.Run(t, tests.ConformanceTests); err != nil { - t.Fatalf("failed to run the gateway conformance test suite: %v", err) - } - - const reportFileName = "apisix-ingress-controller-conformance-report.yaml" - report, err := cSuite.Report() - if err != nil { - t.Fatalf("failed to get the gateway conformance test report: %v", err) - } - - rawReport, err := yaml.Marshal(report) - if err != nil { - t.Fatalf("failed to marshal the gateway conformance test report: %v", err) - } - // Save report in root of the repository, file name is in .gitignore. - require.NoError(t, os.WriteFile("../../"+reportFileName, rawReport, 0o600)) -} diff --git a/test/conformance/suite_test.go b/test/conformance/suite_test.go deleted file mode 100644 index f9f39727f..000000000 --- a/test/conformance/suite_test.go +++ /dev/null @@ -1,256 +0,0 @@ -// 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 conformance - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/gruntwork-io/terratest/modules/retry" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "sigs.k8s.io/controller-runtime/pkg/client" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" -) - -var gatewayClassName = "apisix" -var controllerName = "apisix.apache.org/apisix-ingress-controller" - -var gatewayClass = fmt.Sprintf(` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s -spec: - controllerName: %s -`, gatewayClassName, controllerName) - -var gatewayProxyYaml = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: conformance-gateway-proxy - namespace: %s -spec: - statusAddress: - - %s - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: %s -` - -type GatewayProxyOpts struct { - StatusAddress string - AdminKey string - AdminEndpoint string -} - -var defaultGatewayProxyOpts GatewayProxyOpts - -func deleteNamespace(kubectl *k8s.KubectlOptions) { - // gateway api conformance test namespaces - namespacesToDelete := []string{ - "gateway-conformance-infra", - "gateway-conformance-web-backend", - "gateway-conformance-app-backend", - "apisix-conformance-test", - } - - for _, ns := range namespacesToDelete { - _, err := k8s.GetNamespaceE(GinkgoT(), kubectl, ns) - if err == nil { - // Namespace exists, delete it - GinkgoT().Logf("Deleting existing namespace: %s", ns) - err := k8s.DeleteNamespaceE(GinkgoT(), kubectl, ns) - if err != nil { - GinkgoT().Logf("Error deleting namespace %s: %v", ns, err) - continue - } - - // Wait for deletion to complete by checking until GetNamespaceE returns an error - _, err = retry.DoWithRetryE( - GinkgoT(), - fmt.Sprintf("Waiting for namespace %s to be deleted", ns), - 30, - 5*time.Second, - func() (string, error) { - _, err := k8s.GetNamespaceE(GinkgoT(), kubectl, ns) - if err != nil { - // Namespace is gone, which is what we want - return "Namespace deleted", nil - } - return "", fmt.Errorf("namespace %s still exists", ns) - }, - ) - - if err != nil { - GinkgoT().Logf("Error waiting for namespace %s to be deleted: %v", ns, err) - } - } else { - GinkgoT().Logf("Namespace %s does not exist or cannot be accessed", ns) - } - } -} - -func TestMain(m *testing.M) { - RegisterFailHandler(Fail) - f := framework.NewFramework() - - f.BeforeSuite() - - // Check and delete specific namespaces if they exist - kubectl := k8s.NewKubectlOptions("", "", "default") - deleteNamespace(kubectl) - - namespace := "apisix-conformance-test" - - k8s.KubectlApplyFromString(GinkgoT(), kubectl, gatewayClass) - defer k8s.KubectlDeleteFromString(GinkgoT(), kubectl, gatewayClass) - k8s.CreateNamespace(GinkgoT(), kubectl, namespace) - defer k8s.DeleteNamespace(GinkgoT(), kubectl, namespace) - - gatewayGroupId := f.CreateNewGatewayGroupWithIngress() - adminKey := f.GetAdminKey(gatewayGroupId) - - svc := f.DeployGateway(framework.DataPlaneDeployOptions{ - Namespace: namespace, - GatewayGroupID: gatewayGroupId, - DPManagerEndpoint: framework.DPManagerTLSEndpoint, - SetEnv: true, - SSLKey: framework.TestKey, - SSLCert: framework.TestCert, - TLSEnabled: true, - ForIngressGatewayGroup: true, - ServiceType: "LoadBalancer", - ServiceHTTPPort: 80, - ServiceHTTPSPort: 443, - }) - - if len(svc.Status.LoadBalancer.Ingress) == 0 { - Fail("No LoadBalancer found for the service") - } - - address := svc.Status.LoadBalancer.Ingress[0].IP - - f.DeployIngress(framework.IngressDeployOpts{ - ControllerName: "apisix.apache.org/apisix-ingress-controller", - AdminKey: adminKey, - AdminTLSVerify: false, - Namespace: namespace, - AdminEnpoint: framework.DashboardTLSEndpoint, - StatusAddress: address, - InitSyncDelay: 1 * time.Minute, - }) - - defaultGatewayProxyOpts = GatewayProxyOpts{ - StatusAddress: address, - AdminKey: adminKey, - AdminEndpoint: framework.DashboardTLSEndpoint, - } - - patchGatewaysForConformanceTest(context.Background(), f.K8sClient) - - code := m.Run() - - f.AfterSuite() - - os.Exit(code) -} - -func patchGatewaysForConformanceTest(ctx context.Context, k8sClient client.Client) { - var gatewayProxyMap = make(map[string]bool) - - // list all gateways and patch them - patchGateway := func(ctx context.Context, k8sClient client.Client) bool { - gatewayList := &gatewayv1.GatewayList{} - if err := k8sClient.List(ctx, gatewayList); err != nil { - return false - } - - patched := false - for i := range gatewayList.Items { - gateway := &gatewayList.Items[i] - - // check if the gateway already has infrastructure.parametersRef - if gateway.Spec.Infrastructure != nil && - gateway.Spec.Infrastructure.ParametersRef != nil { - continue - } - - GinkgoT().Logf("Patching Gateway %s", gateway.Name) - // check if the gateway proxy has been created, if not, create it - if !gatewayProxyMap[gateway.Namespace] { - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, - gateway.Namespace, - defaultGatewayProxyOpts.StatusAddress, - defaultGatewayProxyOpts.AdminEndpoint, - defaultGatewayProxyOpts.AdminKey) - kubectl := k8s.NewKubectlOptions("", "", gateway.Namespace) - k8s.KubectlApplyFromString(GinkgoT(), kubectl, gatewayProxy) - - // Mark this namespace as having a GatewayProxy - gatewayProxyMap[gateway.Namespace] = true - } - - // add infrastructure.parametersRef - gateway.Spec.Infrastructure = &gatewayv1.GatewayInfrastructure{ - ParametersRef: &gatewayv1.LocalParametersReference{ - Group: "apisix.apache.org", - Kind: "GatewayProxy", - Name: "conformance-gateway-proxy", - }, - } - - if err := k8sClient.Update(ctx, gateway); err != nil { - GinkgoT().Logf("Failed to patch Gateway %s: %v", gateway.Name, err) - continue - } - - patched = true - GinkgoT().Logf("Successfully patched Gateway %s with GatewayProxy reference", gateway.Name) - } - - return patched - } - - // continuously monitor and patch gateway resources - go func() { - ticker := time.NewTicker(2 * time.Second) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - // clean up the gateway proxy - for namespace := range gatewayProxyMap { - kubectl := k8s.NewKubectlOptions("", "", namespace) - _ = k8s.RunKubectlE(GinkgoT(), kubectl, "delete", "gatewayproxy", "conformance-gateway-proxy") - } - return - case <-ticker.C: - patchGateway(ctx, k8sClient) - } - } - }() -} diff --git a/test/e2e/adminapi/dashboard_api.go b/test/e2e/adminapi/dashboard_api.go deleted file mode 100644 index a937ea440..000000000 --- a/test/e2e/adminapi/dashboard_api.go +++ /dev/null @@ -1,232 +0,0 @@ -// 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 adminapi - -import ( - "context" - "time" - - . "github.com/onsi/ginkgo/v2" //nolint:staticcheck - . "github.com/onsi/gomega" //nolint:staticcheck - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = PDescribe("Test Dashboard admin-api sdk", func() { - s := scaffold.NewDefaultScaffold() - - Context("Service and Route", func() { - var ( - serviceDefault = &v1.Service{ - Metadata: v1.Metadata{ - ID: "test-httpbin-id", - Name: "test-httpbin", - }, - Upstream: &v1.Upstream{ - Type: "roundrobin", - Nodes: []v1.UpstreamNode{ - { - Host: "httpbin-service-e2e-test", - Port: 80, - Weight: 1, - }, - }, - }, - } - routeDefault = &v1.Route{ - Metadata: v1.Metadata{ - Name: "test-route", - ID: "test-routes", - }, - ServiceID: "test-httpbin-id", - Paths: []string{"/headers", "/ip", "/get"}, - } - - routeAnything = &v1.Route{ - Metadata: v1.Metadata{ - Name: "test-route", - ID: "test-route-id", - }, - ServiceID: "test-httpbin-id", - Paths: []string{"/anything"}, - } - ) - - It("Test service and route", func() { - By("create service and route") - _, err := s.DefaultDataplaneResource().Service().Create(context.Background(), serviceDefault) - Expect(err).ToNot(HaveOccurred()) - - _, err = s.DefaultDataplaneResource().Route().Create(context.Background(), routeDefault) - Expect(err).ToNot(HaveOccurred()) - - // TODO: use control-api to check the service and route - time.Sleep(6 * time.Second) - - s.NewAPISIXClient(). - GET("/headers"). - WithHeader("Test-Header", "t1"). - Expect(). - Status(200). - Body(). - Contains("Test-Header") - - By("enable plugin in route") - route2 := routeDefault.DeepCopy() - route2.Plugins = v1.Plugins{ - "proxy-rewrite": map[string]any{ - "headers": map[string]any{ - "add": map[string]any{ - "X-Header-1": "v1", - }, - }, - }, - } - _, err = s.DefaultDataplaneResource().Route().Update(context.Background(), route2) - Expect(err).ToNot(HaveOccurred()) - - time.Sleep(6 * time.Second) - - s.NewAPISIXClient(). - GET("/headers"). - Expect(). - Status(200). - Body(). - Contains("X-Header-1") - - By("create another route") - _, err = s.DefaultDataplaneResource().Route().Create(context.Background(), routeAnything) - Expect(err).ToNot(HaveOccurred()) - - time.Sleep(6 * time.Second) - - s.NewAPISIXClient(). - GET("/anything"). - Expect(). - Status(200) - - By("enable plugin in service") - service2 := serviceDefault.DeepCopy() - service2.Plugins = v1.Plugins{ - "proxy-rewrite": map[string]any{ - "headers": map[string]any{ - "add": map[string]any{ - "X-Header-2": "v2", - }, - }, - }, - } - - _, err = s.DefaultDataplaneResource().Service().Update(context.Background(), service2) - Expect(err).ToNot(HaveOccurred()) - - time.Sleep(6 * time.Second) - - s.NewAPISIXClient(). - GET("/headers"). - Expect(). - Status(200). - Body(). - Contains("X-Header-1"). - NotContains("X-Header-2") - - s.NewAPISIXClient(). - GET("/anything"). - Expect(). - Status(200). - Body(). - Contains("X-Header-2"). - NotContains("X-Header-1") - - By("delete service and route") - - err = s.DefaultDataplaneResource().Route().Delete(context.Background(), routeDefault) - Expect(err).ToNot(HaveOccurred()) - err = s.DefaultDataplaneResource().Route().Delete(context.Background(), routeAnything) - Expect(err).ToNot(HaveOccurred()) - err = s.DefaultDataplaneResource().Service().Delete(context.Background(), serviceDefault) - Expect(err).ToNot(HaveOccurred()) - - routes, err := s.DefaultDataplaneResource().Route().List(context.Background()) - Expect(err).ToNot(HaveOccurred()) - Expect(routes).To(BeEmpty()) - - services, err := s.DefaultDataplaneResource().Service().List(context.Background()) - Expect(err).ToNot(HaveOccurred()) - Expect(services).To(BeEmpty()) - }) - - It("Test apply config with https", func() { - By("create service and route") - _, err := s.DefaultDataplaneResourceHTTPS().Service().Create(context.Background(), serviceDefault) - Expect(err).ToNot(HaveOccurred()) - - _, err = s.DefaultDataplaneResourceHTTPS().Route().Create(context.Background(), routeDefault) - Expect(err).ToNot(HaveOccurred()) - - // TODO: use control-api to check the service and route - time.Sleep(6 * time.Second) - - s.NewAPISIXClient(). - GET("/headers"). - WithHeader("Test-Header", "t1"). - Expect(). - Status(200). - Body(). - Contains("Test-Header") - }) - }) - - Context("Test Plugin metadata", func() { - It("Update plugin meatadata", func() { - // update plugin metadata - datadog := &v1.PluginMetadata{ - Name: "datadog", - Metadata: map[string]any{ - "host": "172.168.45.29", - "port": float64(8126), - "constant_tags": []any{ - "source:apisix", - "service:custom", - }, - "namespace": "apisix", - }, - } - - _, err := s.DefaultDataplaneResource().PluginMetadata().Update(context.Background(), datadog) - Expect(err).ToNot(HaveOccurred()) - - // TODO: use control-api to check the plugin metadata - time.Sleep(6 * time.Second) - - // update plugin metadata - updated := &v1.PluginMetadata{ - Name: "datadog", - Metadata: map[string]any{ - "host": "127.0.0.1", - "port": float64(8126), - "constant_tags": []any{ - "source:ingress", - "service:custom", - }, - "namespace": "ingress", - }, - } - _, err = s.DefaultDataplaneResource().PluginMetadata().Update(context.Background(), updated) - Expect(err).ToNot(HaveOccurred()) - - // TODO: use control-api to check the plugin metadata - }) - }) -}) diff --git a/test/e2e/crds/backendtrafficpolicy.go b/test/e2e/crds/backendtrafficpolicy.go deleted file mode 100644 index 9adb51ed7..000000000 --- a/test/e2e/crds/backendtrafficpolicy.go +++ /dev/null @@ -1,294 +0,0 @@ -// 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 gatewayapi - -import ( - "fmt" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Test BackendTrafficPolicy base on HTTPRoute", func() { - s := scaffold.NewDefaultScaffold() - - var defaultGatewayProxy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s -spec: - controllerName: %s -` - - var defaultGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: %s - listeners: - - name: http1 - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var defaultHTTPRoute = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - "httpbin.org" - rules: - - matches: - - path: - type: Exact - value: /get - - path: - type: Exact - value: /headers - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - Context("Rewrite Upstream Host", func() { - var createUpstreamHost = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: BackendTrafficPolicy -metadata: - name: httpbin -spec: - targetRefs: - - name: httpbin-service-e2e-test - kind: Service - group: "" - passHost: rewrite - upstreamHost: httpbin.example.com -` - - var updateUpstreamHost = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: BackendTrafficPolicy -metadata: - name: httpbin -spec: - targetRefs: - - name: httpbin-service-e2e-test - kind: Service - group: "" - passHost: rewrite - upstreamHost: httpbin.update.example.com -` - - BeforeEach(func() { - s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) - }) - It("should rewrite upstream host", func() { - s.ResourceApplied("BackendTrafficPolicy", "httpbin", createUpstreamHost, 1) - s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.org"). - Expect(). - Status(200). - Body().Contains("httpbin.example.com") - - s.ResourceApplied("BackendTrafficPolicy", "httpbin", updateUpstreamHost, 2) - s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.org"). - Expect(). - Status(200). - Body().Contains("httpbin.update.example.com") - - err := s.DeleteResourceFromString(createUpstreamHost) - Expect(err).NotTo(HaveOccurred(), "deleting BackendTrafficPolicy") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.org"). - Expect(). - Status(200). - Body(). - NotContains("httpbin.update.example.com"). - NotContains("httpbin.example.com") - }) - }) -}) - -var _ = Describe("Test BackendTrafficPolicy base on Ingress", func() { - s := scaffold.NewScaffold(&scaffold.Options{ - ControllerName: "apisix.apache.org/apisix-ingress-controller", - }) - - var defaultGatewayProxy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config - namespace: default -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - var defaultIngressClass = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix-default - annotations: - ingressclass.kubernetes.io/is-default-class: "true" -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: "default" - scope: "Namespace" -` - - var defaultIngress = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress-default -spec: - rules: - - host: httpbin.org - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - var beforeEach = func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(defaultGatewayProxy, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - - By("create IngressClass with GatewayProxy reference") - err = s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") - Expect(err).NotTo(HaveOccurred(), "creating IngressClass with GatewayProxy") - - By("create Ingress with GatewayProxy IngressClass") - err = s.CreateResourceFromString(defaultIngress) - Expect(err).NotTo(HaveOccurred(), "creating Ingress with GatewayProxy IngressClass") - time.Sleep(5 * time.Second) - } - - Context("Rewrite Upstream Host", func() { - var createUpstreamHost = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: BackendTrafficPolicy -metadata: - name: httpbin -spec: - targetRefs: - - name: httpbin-service-e2e-test - kind: Service - group: "" - passHost: rewrite - upstreamHost: httpbin.example.com -` - - var updateUpstreamHost = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: BackendTrafficPolicy -metadata: - name: httpbin -spec: - targetRefs: - - name: httpbin-service-e2e-test - kind: Service - group: "" - passHost: rewrite - upstreamHost: httpbin.update.example.com -` - - BeforeEach(beforeEach) - It("should rewrite upstream host", func() { - s.ResourceApplied("BackendTrafficPolicy", "httpbin", createUpstreamHost, 1) - s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.org"). - Expect(). - Status(200). - Body().Contains("httpbin.example.com") - - s.ResourceApplied("BackendTrafficPolicy", "httpbin", updateUpstreamHost, 2) - s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.org"). - Expect(). - Status(200). - Body().Contains("httpbin.update.example.com") - - err := s.DeleteResourceFromString(createUpstreamHost) - Expect(err).NotTo(HaveOccurred(), "deleting BackendTrafficPolicy") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.org"). - Expect(). - Status(200). - Body(). - NotContains("httpbin.update.example.com"). - NotContains("httpbin.example.com") - }) - }) -}) diff --git a/test/e2e/crds/consumer.go b/test/e2e/crds/consumer.go deleted file mode 100644 index 2666a0570..000000000 --- a/test/e2e/crds/consumer.go +++ /dev/null @@ -1,511 +0,0 @@ -// 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 gatewayapi - -import ( - "fmt" - "net/http" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Test Consumer", func() { - s := scaffold.NewDefaultScaffold() - - var defaultGatewayProxy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s -spec: - controllerName: %s -` - - var defaultGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: %s - listeners: - - name: http1 - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var defaultHTTPRoute = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: PluginConfig -metadata: - name: auth-plugin-config -spec: - plugins: - - name: multi-auth - config: - auth_plugins: - - basic-auth: {} - - key-auth: - header: apikey ---- - -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - "httpbin.org" - rules: - - matches: - - path: - type: Exact - value: /get - filters: - - type: ExtensionRef - extensionRef: - group: apisix.apache.org - kind: PluginConfig - name: auth-plugin-config - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - Context("Consumer plugins", func() { - var limitCountConsumer = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: Consumer -metadata: - name: consumer-sample -spec: - gatewayRef: - name: apisix - credentials: - - type: key-auth - name: key-auth-sample - config: - key: sample-key - plugins: - - name: limit-count - config: - count: 2 - time_window: 60 - rejected_code: 503 - key: remote_addr -` - - var unlimitConsumer = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: Consumer -metadata: - name: consumer-sample2 -spec: - gatewayRef: - name: apisix - credentials: - - type: key-auth - name: key-auth-sample - config: - key: sample-key2 -` - - BeforeEach(func() { - s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) - }) - - It("limit-count plugin", func() { - s.ResourceApplied("Consumer", "consumer-sample", limitCountConsumer, 1) - s.ResourceApplied("Consumer", "consumer-sample2", unlimitConsumer, 1) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - By("trigger limit-count") - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(503) - - for i := 0; i < 10; i++ { - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key2"). - WithHost("httpbin.org"). - Expect(). - Status(200) - } - }) - }) - - Context("Credential", func() { - var defaultCredential = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: Consumer -metadata: - name: consumer-sample -spec: - gatewayRef: - name: apisix - credentials: - - type: basic-auth - name: basic-auth-sample - config: - username: sample-user - password: sample-password - - type: key-auth - name: key-auth-sample - config: - key: sample-key - - type: key-auth - name: key-auth-sample2 - config: - key: sample-key2 -` - var updateCredential = `apiVersion: apisix.apache.org/v1alpha1 -kind: Consumer -metadata: - name: consumer-sample -spec: - gatewayRef: - name: apisix - credentials: - - type: basic-auth - name: basic-auth-sample - config: - username: sample-user - password: sample-password - plugins: - - name: key-auth - config: - key: consumer-key -` - - BeforeEach(func() { - s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) - }) - - It("Create/Update/Delete", func() { - s.ResourceApplied("Consumer", "consumer-sample", defaultCredential, 1) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key2"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - By("update Consumer") - s.ResourceApplied("Consumer", "consumer-sample", updateCredential, 2) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(401) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key2"). - WithHost("httpbin.org"). - Expect(). - Status(401) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "consumer-key"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - By("delete Consumer") - err := s.DeleteResourceFromString(updateCredential) - Expect(err).NotTo(HaveOccurred(), "deleting Consumer") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(401) - }) - }) - - Context("SecretRef", func() { - var keyAuthSecret = ` -apiVersion: v1 -kind: Secret -metadata: - name: key-auth-secret -data: - key: c2FtcGxlLWtleQ== -` - var basicAuthSecret = ` -apiVersion: v1 -kind: Secret -metadata: - name: basic-auth-secret -data: - username: c2FtcGxlLXVzZXI= - password: c2FtcGxlLXBhc3N3b3Jk -` - const basicAuthSecret2 = ` -apiVersion: v1 -kind: Secret -metadata: - name: basic-auth-secret -data: - username: c2FtcGxlLXVzZXI= - password: c2FtcGxlLXBhc3N3b3JkLW5ldw== -` - var defaultConsumer = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: Consumer -metadata: - name: consumer-sample -spec: - gatewayRef: - name: apisix - credentials: - - type: basic-auth - name: basic-auth-sample - secretRef: - name: basic-auth-secret - - type: key-auth - name: key-auth-sample - secretRef: - name: key-auth-secret - - type: key-auth - name: key-auth-sample2 - config: - key: sample-key2 -` - - BeforeEach(func() { - s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) - }) - It("Create/Update/Delete", func() { - err := s.CreateResourceFromString(keyAuthSecret) - Expect(err).NotTo(HaveOccurred(), "creating key-auth secret") - err = s.CreateResourceFromString(basicAuthSecret) - Expect(err).NotTo(HaveOccurred(), "creating basic-auth secret") - s.ResourceApplied("Consumer", "consumer-sample", defaultConsumer, 1) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - // update basic-auth password - err = s.CreateResourceFromString(basicAuthSecret2) - Expect(err).NotTo(HaveOccurred(), "creating basic-auth secret") - - // use the old password will get 401 - Eventually(func() int { - return s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second). - Should(Equal(http.StatusUnauthorized)) - - // use the new password will get 200 - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password-new"). - WithHost("httpbin.org"). - Expect(). - Status(http.StatusOK) - - By("delete consumer") - err = s.DeleteResourceFromString(defaultConsumer) - Expect(err).NotTo(HaveOccurred(), "deleting consumer") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient(). - GET("/get"). - WithHeader("apikey", "sample-key"). - WithHost("httpbin.org"). - Expect(). - Status(401) - - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(401) - }) - }) - - Context("Consumer with GatewayProxy Update", func() { - var additionalGatewayGroupID string - - var defaultCredential = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: Consumer -metadata: - name: consumer-sample -spec: - gatewayRef: - name: apisix - credentials: - - type: basic-auth - name: basic-auth-sample - config: - username: sample-user - password: sample-password -` - var updatedGatewayProxy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - BeforeEach(func() { - s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) - }) - - It("Should sync consumer when GatewayProxy is updated", func() { - s.ResourceApplied("Consumer", "consumer-sample", defaultCredential, 1) - - // verify basic-auth works - s.NewAPISIXClient(). - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(200) - - By("create additional gateway group to get new admin key") - var err error - additionalGatewayGroupID, _, err = s.CreateAdditionalGatewayGroup("gateway-proxy-update") - Expect(err).NotTo(HaveOccurred(), "creating additional gateway group") - - resources, exists := s.GetAdditionalGatewayGroup(additionalGatewayGroupID) - Expect(exists).To(BeTrue(), "additional gateway group should exist") - - client, err := s.NewAPISIXClientForGatewayGroup(additionalGatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "creating APISIX client for additional gateway group") - - By("Consumer not found for additional gateway group") - client. - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(404) - - By("update GatewayProxy with new admin key") - updatedProxy := fmt.Sprintf(updatedGatewayProxy, framework.DashboardTLSEndpoint, resources.AdminAPIKey) - err = s.CreateResourceFromString(updatedProxy) - Expect(err).NotTo(HaveOccurred(), "updating GatewayProxy") - time.Sleep(5 * time.Second) - - By("verify Consumer works for additional gateway group") - client. - GET("/get"). - WithBasicAuth("sample-user", "sample-password"). - WithHost("httpbin.org"). - Expect(). - Status(200) - }) - }) -}) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go deleted file mode 100644 index 6d80833c6..000000000 --- a/test/e2e/e2e_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 e2e - -import ( - "fmt" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - _ "github.com/apache/apisix-ingress-controller/test/e2e/adminapi" - _ "github.com/apache/apisix-ingress-controller/test/e2e/crds" - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - _ "github.com/apache/apisix-ingress-controller/test/e2e/gatewayapi" - _ "github.com/apache/apisix-ingress-controller/test/e2e/ingress" -) - -// Run e2e tests using the Ginkgo runner. -func TestE2E(t *testing.T) { - RegisterFailHandler(Fail) - f := framework.NewFramework() - - BeforeSuite(f.BeforeSuite) - AfterSuite(f.AfterSuite) - - _, _ = fmt.Fprintf(GinkgoWriter, "Starting apisix-ingress suite\n") - RunSpecs(t, "e2e suite") -} diff --git a/test/e2e/framework/consts.go b/test/e2e/framework/consts.go deleted file mode 100644 index d4ad9ba3f..000000000 --- a/test/e2e/framework/consts.go +++ /dev/null @@ -1,234 +0,0 @@ -// 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 framework - -import ( - _ "embed" -) - -const ( - DashboardEndpoint = "http://api7ee3-dashboard.api7-ee-e2e:7080" - DashboardTLSEndpoint = "https://api7ee3-dashboard.api7-ee-e2e:7443" - DPManagerTLSEndpoint = "https://api7ee3-dp-manager.api7-ee-e2e:7943" -) - -var ( - //go:embed manifests/cert.pem - TestServerCert string - //go:embed manifests/key.pem - TestServerKey string -) - -const ( - TestCACert = `-----BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIUBB5PHXyymeboPDVdYeYihYnm5XIwDQYJKoZIhvcNAQEL -BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 -MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNv -bTAeFw0yNDA2MjYxNTQ5NTBaFw0zNDA2MjQxNTQ5NTBaMF0xCzAJBgNVBAYTAkNO -MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p -emF0aW9uMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCutPQZr71VAdVvLd65agMsj9xD46jdZxWzP1unfv34 -6VFhCFPJp39TuBkwcXmSEmSzCcQXyCvWhRV+PErr+N4dadUpoci2E/CcKAlOisxz -OaPz52yS+h5kf8wpVS3Tk9JHONZp6P3HLY2yeUtRl/Bw3Fyo7nIJCtwx5jJ2cFgE -dvqfruiZYTSU5vxvS2QDXvX0gcOuR4uP+RRBcwlY+oD+G0pP4vsUcLpCQpD58p1c -RSLwoX6dPb3eohCXufCnskIwiIxC3jQcVrI1gOggJkZuGXNRh0aS5O7L0iu37l57 -UStFB4kjbnCWterNz/NRRhD2Ad8RTALnJMeGKb7uH9OzAgMBAAGjLzAtMAwGA1Ud -EwQFMAMBAf8wHQYDVR0OBBYEFH78Ns0zkjTuK1EhiLMNYnXUZatxMA0GCSqGSIb3 -DQEBCwUAA4IBAQCVhAzUb32Qyjn5oZHsDYKaQIHfXe+/W2oM41dDTSxjFlbvBjaq -JWxgAYBA5l28b+e9zUK2BTcSNzVbrfm5/qoykAQNaR4Vvhy3LxFyOd6G87as3+hv -jlerjSa/gh8XCPFzs2t6wyhZqEgcNZBK6oagnaxKstoS2jXjAL+7dx4PRBdw7MTq -joQ+TzLgsB9kFMnihmR+LpDFfQCqAfp5X0z9RLgnH0zVcBrRXKKb8AaOWBdkdK6g -BLIW7+4ZxW5BzYmi6ZuDDjP96wLpWT7boJPi3BqnCEQIzywNMBbqZO9LiWdGf0TH -EpkzMRsCTGGar43HkQgDZdjicRKiuWYFO47O ------END CERTIFICATE-----` - - TestCAKey = `-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCutPQZr71VAdVv -Ld65agMsj9xD46jdZxWzP1unfv346VFhCFPJp39TuBkwcXmSEmSzCcQXyCvWhRV+ -PErr+N4dadUpoci2E/CcKAlOisxzOaPz52yS+h5kf8wpVS3Tk9JHONZp6P3HLY2y -eUtRl/Bw3Fyo7nIJCtwx5jJ2cFgEdvqfruiZYTSU5vxvS2QDXvX0gcOuR4uP+RRB -cwlY+oD+G0pP4vsUcLpCQpD58p1cRSLwoX6dPb3eohCXufCnskIwiIxC3jQcVrI1 -gOggJkZuGXNRh0aS5O7L0iu37l57UStFB4kjbnCWterNz/NRRhD2Ad8RTALnJMeG -Kb7uH9OzAgMBAAECggEAQb6h73qlZrSCc8zQuHivChl3G+sz1GGjFmm83YraG03+ -DGRcV5IbRc+NVyAUzkXytDd0Hjj7WkaJwJAC58snFu2JRJn31KErVjBw1ChCaQgj -bTlFMAhE4LABDfrafHjv1FKMyZ1exxIa9TNVBzcEygv7KK1Wp5V5KKQGkHCVhtP5 -PDqKwyiUqFpsM7Codr8TmavHykSfVRhxPifDNXMDMXsSUT/2dFj0QXljA3tjzk6d -UHzx4z44cODbjWE74ZeQw2SFslKHgK0ZVYivE6+f3L/p7fSqq7hJ6T0BXpKolbHZ -yU7Xh6BBy0WKqACkUWALZ6tglcb+KoTqhZ/fTZW6mQKBgQDr6ivPmQI4deIbWGyi -EGfzrLfiVEuqCsz5gX88nvBRoGevKi7kCbIf/IoNGIm5SJ8gJDs6eSPKFwAEd29H -N2muFXmune8g7lVZjQ8GPGUu4IvJMS7OAcbLmpI4pUaVGtJsh2fmXnaTSR5D5y64 -XtPGdkluLr/B5vz/X0D0NG80GQKBgQC9lL04p1D6kJj+JwyNm7bZ1OhgpH5Pup82 -Ia+5GH8m7VLS4/PrpllOmhgccaCJK7M36EXLELHRHpmOoLzUiemgtnoQ0JMmeWfN -pVigWdIsCMSS1wJLNklr17eDDgvdcs0W3cujja4/2LlksNY8/zh7LJ/k/YUTnlkG -VsSeLfEfqwKBgQCNinmunAaRCWkXLv4+XcmAkWfiCuE6rDA+oktMe6+DydFrbsuj -VY3hUwsgwFAhMkkGZ7aBZpzqatI/28iP2dc18vyGn4sRHu1mRRN2klXCwkYb9741 -KyuyjJKeGcs3Olh1dOgJdzN9OqlF5DZLt9kngWCdEr9J/uRb8zJtUehGQQKBgEuD -yM+dThNQr7Bk64oooXApb5q3Sx0FEFAmoPFQwa1G0Tvx0wJl06MMnFgQJssc3hmB -6vMVJk9PKgl3G2BpwubiaMLz4flsWJ3ApAnTXXVu1KZNALvm1t4fIhkQ6kb+aJUY -KfpvAB6sfESQb/YCD4R45QP4vB5xb7Kns0/yqt5bAoGAQ0kPRvAbdmBURLscsOpN -f4sdXsJLqxAppIqrYolUVqIQPAFYT7oWLpSMmIqzjFzRFBj3ZUf3fZCNCi2kkRvs -60VyICYZobrnbcbkCx7qydIuhr2+8301lcginh8DIN58c0IZmoIRv/Z44SPfl1ku -zDGD28KCK1oiIZOe9sgcgf4= ------END PRIVATE KEY-----` - - TESTCert1 = `-----BEGIN CERTIFICATE----- -MIIDxDCCAqygAwIBAgIUO1cgq5OMm6h5eb5U+r3DftaM7e0wDQYJKoZIhvcNAQEL -BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 -MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNv -bTAeFw0yNDA2MjYxNzA4MzFaFw0yODExMTIxNzA4MzFaMGAxCzAJBgNVBAYTAkNO -MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p -emF0aW9uMRswGQYDVQQDDBJzZXJ2ZXIuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQC+nRQiu87exjwtnTb1+dBFdMffucW84qPZ3TZY -e3beLJkIjM2eeptzEVf5eFAxo9lXpi9DKJQopI9aRc3SvyWRg9bj8wm88UdA/2LO -8q1oRaDzKHlMfSVqjdZ8qTalbd4FzhUWw9TWKGQxhA5yypisuQjOrVDC6bvk3WMk -BoD+2zil+q05nRjUSCbGkKO0HecXt2M2jq1nyN68J5ymtmooUbSM7TAC+ymBggJc -JsgJ3DrquMNqQykjsSotVaz+y857DedzJoMoMgIvJy1cNexpY6e/PK2oO9OB+OTg -Rq/XtC/wQ5ZJ0/rY1q1yU8t5JmlVkrV6RPUT5SuoVeA8Fdu7AgMBAAGjeTB3MDUG -A1UdEQQuMCyCEnNlcnZlci5leGFtcGxlLmNvbYIWd3d3LnNlcnZlci5leGFtcGxl -LmNvbTAdBgNVHQ4EFgQUZ3hBoewum8wvVnSPLiaAI1S22cwwHwYDVR0jBBgwFoAU -fvw2zTOSNO4rUSGIsw1iddRlq3EwDQYJKoZIhvcNAQELBQADggEBAC2LQ/nLc1PP -ioPeqxKwF094yrifdhZGCmCSFpsnPbxhgxTRKSSMe7+XPadS4xd4VeRkbmuyDuUg -kYCAr3eTpSKfc3cTHP4S/+DDPefUn8u5lbPEE1Aq2JMNubXwCUMy+hNgX7dHWzBW -sqR+GErLzGGsfkTWhIxwH8Vx/hhKS/Kv5EEvZ42HrvL3570/04zq1tUYPlqPoQBc -t+6M2fJQx6lYdVjtYssm/6MnjNIM59NmmmwwrLZZyB96kDAW8xFndzcJQv4uojdb -UjWkMt/J7i6TWZY9DrSmAwCo2ZDCUZT5vQUkmILc9st/ie8v3755lJxoAOIyxEmi -Z+TO4JGixxQ= ------END CERTIFICATE-----` - TestKey1 = `-----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+nRQiu87exjwt -nTb1+dBFdMffucW84qPZ3TZYe3beLJkIjM2eeptzEVf5eFAxo9lXpi9DKJQopI9a -Rc3SvyWRg9bj8wm88UdA/2LO8q1oRaDzKHlMfSVqjdZ8qTalbd4FzhUWw9TWKGQx -hA5yypisuQjOrVDC6bvk3WMkBoD+2zil+q05nRjUSCbGkKO0HecXt2M2jq1nyN68 -J5ymtmooUbSM7TAC+ymBggJcJsgJ3DrquMNqQykjsSotVaz+y857DedzJoMoMgIv -Jy1cNexpY6e/PK2oO9OB+OTgRq/XtC/wQ5ZJ0/rY1q1yU8t5JmlVkrV6RPUT5Suo -VeA8Fdu7AgMBAAECggEASdlzxq06zebBw+5oL86UmYRQN+ayrKamUq848fkLqbJf -rAdZVrAr793lVrr9Xu4bM7EoGH3tQP3YqGHpB2CVPpZ0uCYePLzCHXWUo5c0BfUM -EYk5zZ+i0nCXi/7HNDqnzVn1o7dFi59kiiaermy90BV0Sxas9oc2C8qWMYvviE1d -GS0/Prmma+H0gZDTiQTrQsR++HmSQ3jvu9LYvVx4AkNveYNbPgF1o52PgEryTH2Y -4tvHuyp7KpjSSNKoRuXw25OpVLsPXaxDFnSoe8YK6ltIA4kG6f7G7JbufKdaiCbY -c4d6co5xTJa77NMGzY3j55gTFU3mfhMC9QvrvcVycQKBgQDv7LvV2LNf6ixlBWXO -t3TAEOMM2x6awmDDPDU8i1uIhHHA7ZqsHREIC33Sily+1NwOm/iuLWTlHv0+4cww -y75Xvl8e2mDPtkoT8wNeHdKXTp4CLzrIvEHzG0qf063ob0hP24ymqy+PgO/WW8Pl -K/iWTpTzc9UKUuhiNPzp2XIMYwKBgQDLYotPuKkQHwGrhI8mZKKTcefzN9Ton2Kw -7qJ3qDPrbDS0yjbU5+TKaNjKlZ+fsPQkHb5aWkGK+hYU6x7X8V/MZrc5MSwtNjPI -QfhTPKsxNSAPqRoTwP+QVXECQhCdjRZCpE6+4aaB/31VbIjTWdhxUyc6zrPLDimX -p0hhEcH2yQKBgAnBna3HfxvSYPXGr2oliajZxvHZ4ze12ct2ok+Q9yro/9sxjk2b -bPrfxMEQAU99RmmNrCIhFG5AwVmSQwRk9JuK0UFm7fLkXcTL6AImwk6G0uQR2Zka -FrB1FqbDK9o81DrzGZgZc/io7JfR6XhjPluWXHY96pbd4jdEIli8D+gzAoGAAn0o -O0eFOh9HA/RRVCTzIF7Ked17C4W3zXZ+Iny6de0TEAtRdHWKBTgXPxNpqqidtDtw -8uYb2zmIP6VI8VeQ1o2DPH3vjnYVWCQGh+48IhQGWmq1WPyJpBiHk4F/do4dcZ9V -H1zfjsOzovH7EqsMzQY5eqzA4oE/3Q09A4MWHpECgYAU3uxxs5g7QwdK01BPXznk -H1y7bdn2LYS5otllOgAfZRX6BUQNFE8RtwA4HNe764SSEkk7EtTckx0d7ar8V1V2 -pfxfd8A0pNI54NtnFk8OS0BVkW5SGoBCWRh2nlV1r0B/7np0X61GgE95JDMslRn0 -0AOPIR/qFJ1YYT0a7yKyjQ== ------END PRIVATE KEY-----` - - TESTCert2 = `-----BEGIN CERTIFICATE----- -MIIDRDCCAiwCFDtXIKuTjJuoeXm+VPq9w37WjO3rMA0GCSqGSIb3DQEBCwUAMF0x -CzAJBgNVBAYTAkNOMQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMG -A1UECgwMT3JnYW5pemF0aW9uMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wHhcN -MjQwNjI2MTcwNTM1WhcNMjgxMTEyMTcwNTM1WjBgMQswCQYDVQQGEwJDTjEOMAwG -A1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkxFTATBgNVBAoMDE9yZ2FuaXphdGlv -bjEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAihDdGYkyQUHVPCAVho2bJFIYMkCALqNbBoLcuQ4UsCzr -rOZlff5CgPEwNshyH2k7jUp0D6i4PArN/y70BxhU6R/I2eKUhhlPdFxtNiEae9jn -/ndOA7eb4WB9eFSHkqncMjc6X4/Fe9x5E2uaIv17atvyQwuT6yL3SOmGRp6LowgC -4IhDwHLGdVEmOGlkbTaXw6Vbpv9ckjObNJ4/IRhIiifdK4NJSJIIaE/Im3SuciFP -6mRIZKPeSrmKaVrpb9ElTx6NcFxAy0OKZQ3I2wnxNkIq0y3HSgkVFNKRdeHq/JQu -+mOad2TAts/YuhY/jwmEz00eUVHZmAmBk33K5o6cjwIDAQABMA0GCSqGSIb3DQEB -CwUAA4IBAQATI/7xbZyGqMT4jqCiWgpj8z5FxPQ7mYyh8AIvmar6tIt1EoRDC9gk -1uAaFMYfdpQpfdM2/0B1aFl7cDArEyOCumlDJmmyURkacqzw0a23ELJnthg40VsN -Zvg9n+z8jcj+dLKCZJuebp978XRKucZaaxI4Z4h7OzKhiJbUpwQUxM4sPuazLPDL -I6BwMatU7saBQM7SVMZJcGsc0XPJXmh292HL6ZqWf/qPWmJklXAldB3rxY8CTxx7 -ucWSym43eXOgsTtgH8xHiRLWpMVjFzl1AZxgl9iCAXXh2s4QjP778VCX7QQFPvsR -0iZCSG0R5BbpDa0NKV5SklBIj8NdMLrA ------END CERTIFICATE-----` - - TestKey2 = `-----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCKEN0ZiTJBQdU8 -IBWGjZskUhgyQIAuo1sGgty5DhSwLOus5mV9/kKA8TA2yHIfaTuNSnQPqLg8Cs3/ -LvQHGFTpH8jZ4pSGGU90XG02IRp72Of+d04Dt5vhYH14VIeSqdwyNzpfj8V73HkT -a5oi/Xtq2/JDC5PrIvdI6YZGnoujCALgiEPAcsZ1USY4aWRtNpfDpVum/1ySM5s0 -nj8hGEiKJ90rg0lIkghoT8ibdK5yIU/qZEhko95KuYppWulv0SVPHo1wXEDLQ4pl -DcjbCfE2QirTLcdKCRUU0pF14er8lC76Y5p3ZMC2z9i6Fj+PCYTPTR5RUdmYCYGT -fcrmjpyPAgMBAAECggEALMvr8S0isSm7FhcS5OVtQQuh7iwfawJ1tlcfZqu66q6o -iSD4LTkiOtQG4L+Ix8hNGjreDXag0FpxHCsP4n1yj3FQqeFqb1gm/4o9pSJ4hVaL -b3UmSu54FVhJXZGPq3aRi3l/5yGu4dFXN4XiLRgxwyo6IKbRB6oAsheTCbbHd83X -kjAMHC+85D4k94oQ2pxTlzz3rRTAPnnVd+A1zZP54pIOtdMWncOBBVtOsaFiLl5o -gHryvLW3i1GUQJAsaPFhGrIleiG/gCSaHNcN33Ky9mnjm8/LlEuJQPxR/gOPlTtJ -raEWD6XsUkg8KgBNERFSA0G51k7XunkNVN0mf11YAQKBgQDB7uhpZTYlhcrhkOUt -6D80iW7zsyhLL26xrc0eHd39yJmGBanoLAe58utR3nZrSLrc2ygb73183VBOAFFL -/VBX2vn3b7hvJfyxpsfqIeIoCmDBUZfZbHRy4z7APXdieUVeseiDjqfDpshwrZ6P -65IgZXlOGxV6AZB+MMaC/Xr7zwKBgQC2QLO2xBNMIdwRij+6zBRNnECg2Uz8vGle -C3fHRodx+RTHKUt08RPbKVlvdJCcvF3kJPXTkJf6jXhEFuzzP60tnw8zztr8lBVp -Mgbx6FeHLwAclGwKUzB/XscFGuCkVnzdRjt6fOp/LdTDtjavnz9+XOBedz0q44OD -gcuP/prDQQKBgEj48JLX9qvf0BtYPiQsA7xtCkbA3ySLUIei8k+mxWU9GieH8gre -pnmdolZH8x8jWszH2ivIzdsyG4l+LRj+1EB6sRwd+bQ5AwCF1SuQXFzcqtOTNCQn -3xlr0KzddBKltyiOoQL/prjEBSAA9G0LIwz1odN19XrXI9uX1tprLt+ZAoGBAIfH -MlxSPbI1c5eZsJsA2YtsgnWTDpkn4TQQRR8wO0487C7oY+/ufwWDC79OT0KFz0le -A8qnaaJfbR8FGWirouBfJNtLG7iatkzV2Xi1ySfMaz/fj5Itoe7ydEEoYf3s9T+A -yGUb1TQEuANKp/k1If+JQBsAqJBgSaVauIZBf5lBAoGBALew21wK6TvLOxXGRShr -uQTHPT5zwebLvFSoc+7fmdoCuGI04OyGUrJR/uCqXMyyP3xFk7FmkjyavpQc9c+R -+tO3iJEqGvyWC24Bzpy5z9NhdapkXKdju6vmftgBn8SOLOMH9am+EAhwKqfwyNIn -jLSA1vUeTvzS6lIWMGpWq2+q ------END PRIVATE KEY-----` -) - -const ( - TestCert = `-----BEGIN CERTIFICATE----- -MIIC1TCCAb2gAwIBAgIJANm/NDY0xwZUMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV -BAMTD3d3dy5leGFtcGxlLmNvbTAeFw0yMTEyMjcwNzI0MTNaFw0zMTEyMjUwNzI0 -MTNaMBoxGDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAPOeWroWLvnbzmRtvYsyRkFVePoMY3LvZqNaxCQpZHD6 -ra/fRDTem01YvJjm5qUwrn9YXKBUgcoTfA3vHGYFHE4lifdfCbxlb0otMCbEdEsX -P8kOMszB5SlxIPiCLVhc1LOKmHDzzw7axrRStbgN/RJUQ9Fp1QXVAnvEMWcLNopD -E7I148dkpHrxmjW8vuB7apWhcVW+QiOYn4rGyqoilhrL4nRCOJiCVqESMgPcu5dO -Dxf6KcAVd/IMMFTQ/X4+e2dUJpYyhCe8ApnCqrumjfXKqIEfyyTCavKeQEfvPgK4 -PhP2BFpWrxRWkn4VVTxIxS0/EVJaAaC/4gmVMeYg+wUCAwEAAaMeMBwwGgYDVR0R -BBMwEYIPd3d3LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQAKiJaa1FNC -p9NwoJvGyhK5UO2Tci3H63xZs2tFj5UZGxAIqJSxVo80ExhUXuDAM3evryM193uz -uNxbB/oIWEMNLBnacXQi8Evob14gkIwRmQ/iACSIGTupazBLwiZM6infPE2/OoYR -YihMgeWtW9U4XOkRhm013GgueeWP8v1jtyB2p3hoLK5UcLOAhkAOaJZXLDW0rznx -jkNC6NcjYvMHkm3bZYqGsRmZfNGvm5rM8s9c3n4MPgWlllt6RuMSimzIRQSKu2E4 -oGKUqgeOOf5BXunHEgkyOTYittlg6MRwBET6ymHjYvwz87Loot7ji2IomyP08jdS -tYKdaDOJg+su ------END CERTIFICATE-----` - - TestKey = `-----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA855auhYu+dvOZG29izJGQVV4+gxjcu9mo1rEJClkcPqtr99E -NN6bTVi8mObmpTCuf1hcoFSByhN8De8cZgUcTiWJ918JvGVvSi0wJsR0Sxc/yQ4y -zMHlKXEg+IItWFzUs4qYcPPPDtrGtFK1uA39ElRD0WnVBdUCe8QxZws2ikMTsjXj -x2SkevGaNby+4HtqlaFxVb5CI5ifisbKqiKWGsvidEI4mIJWoRIyA9y7l04PF/op -wBV38gwwVND9fj57Z1QmljKEJ7wCmcKqu6aN9cqogR/LJMJq8p5AR+8+Arg+E/YE -WlavFFaSfhVVPEjFLT8RUloBoL/iCZUx5iD7BQIDAQABAoIBAGVBMQZdCANTh5IY -RoqfR7IJ+3E6Su9Pb4J/zDwXdCa9GgmaK3gp+bSJKEII3l5UQIKvUDhXR2ac+Je2 -BUCl6SDV22UUfDBwnHPhGj1Ss98t95XyL80I3d1+pqyDNqOeWc2R0lBIFYxgA+yY -3+xy6/d9TH6ylRaKdTDJ15qzf2SxMtR/SiXyILWU7xWiYxINoHh2IVDte/KlNa0q -iCbIiyX1xdYmcD0rCEVxrWlo1XNjmyO/MPTBhJf/DyZhQNHBDJa2fWzbPOr1I+Nb -vh0GiJVwhkENtucnjmt+jCLqTkTNAAv2mJ1DxbY/DcM+TgTxHmAlTpBM0bh3WsS9 -De8hefUCgYEA/b9LP0fVXTv1K/whKcgi0AW1GCcUrdWVrdN3/K+yPnEZBtl8VY5t -SvQkJPkQsVFJWdZUdRDpHhqYFa0I6zIiNF7DbIxF+Ag+N7uiZ6xzP0L9k1wmgKR8 -PT47fJVuHECxgxexz9FGQwXH7eroJjLPEoxD2Z56COVIJOlYO6e9sXMCgYEA9cgK -WxE2NsYIjrgOqs93GKYY+TmmoSHWiy1bl7p3sUolobPThSd31hdk6ZdMlPPbpr3+ -MYgZoFLud+3l+/6+tttGNNVkB6lkVXzd2WWG6xOrErRwYIz57yiWKGLeWg17jXXf -zqjFNTLpd8U9lM8Lf/XNyfs2tU5oxkUD6teCo6cCgYAtwdMl5CQ7ndZGSj8Is8hj -TsQrSNDX0A4fvGSEsoIn9GkY7RsYqohW3dOuvyMddpUNmDK+sX/4J7+JGRzknLPC -UdxXtKvhYEsn7bQJkfVuUPw9GH7w77hfqts7Sg8DFT9tblZoLUrIR0CYTKX0TXE9 -3QFXOtayx/XMgi+hAkyYtQKBgQCtgKGO1/+levbfiR8RhZNVWyuWBBSU+wYxCbv2 -yDNmfClElWVkQhBemfUq0RvGqr8MXmLrJGCyxNiC4PXRhmurOe+9rEYJApNJpfQW -W416tU+2zJnoDp0BL22Q5PqCJ7JokiWEBa/xdhdJ7XsjaWV811CGnUhphQiBroat -aaVXUQKBgDXHbRmEBo/1fB4Gn7i2bjYOl1Z1e3klNvbdMT/ClNSFy8VsU3HP5XoL -jnzTc80ABlT1PQgrnQxhPpL3wbkSyv0lux5mcM0U89KxpR/SLlvVFAag6UWODt53 -hhdq+X/vrgK+uicSx8Q1zL2iCLdfsZ0fPryMdTZrN3ytEBEWMPeX ------END RSA PRIVATE KEY-----` -) diff --git a/test/e2e/framework/dashboard.go b/test/e2e/framework/dashboard.go deleted file mode 100644 index d3006a6da..000000000 --- a/test/e2e/framework/dashboard.go +++ /dev/null @@ -1,463 +0,0 @@ -// 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 framework - -import ( - "bytes" - _ "embed" - "encoding/json" - "fmt" - "os" - "text/template" - "time" - - "github.com/api7/gopkg/pkg/log" - "github.com/google/uuid" - . "github.com/onsi/gomega" - "golang.org/x/net/html" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/kube" - "k8s.io/apimachinery/pkg/util/yaml" - - v1 "github.com/apache/apisix-ingress-controller/api/dashboard/v1" -) - -var ( - API7EELicense string - - valuesTemplate *template.Template - - dashboardVersion string -) - -func init() { - API7EELicense = os.Getenv("API7_EE_LICENSE") - if API7EELicense == "" { - panic("env {API7_EE_LICENSE} is required") - } - - dashboardVersion = os.Getenv("DASHBOARD_VERSION") - if dashboardVersion == "" { - dashboardVersion = "dev" - } - - tmpl, err := template.New("values.yaml").Parse(` -dashboard: - image: - repository: hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated - pullPolicy: IfNotPresent - tag: {{ .Tag }} - extraEnvVars: - - name: GOCOVERDIR - value: /app/covdatafiles - extraVolumes: - - name: cover - hostPath: - path: /tmp/covdatafiles - type: DirectoryOrCreate - extraVolumeMounts: - - name: cover - mountPath: /app/covdatafiles -dp_manager: - image: - repository: hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager - pullPolicy: IfNotPresent - tag: {{ .Tag }} - extraEnvVars: - - name: GOCOVERDIR - value: /app/covdatafiles - extraVolumes: - - name: cover - hostPath: - path: /tmp/covdatafiles - type: DirectoryOrCreate - extraVolumeMounts: - - name: cover - mountPath: /app/covdatafiles -fullnameOverride: api7ee3 -podSecurityContext: - runAsUser: 0 -dashboard_configuration: - log: - level: debug - database: - dsn: {{ .DSN }} - server: - listen: - disable: false - host: "0.0.0.0" - port: 7080 - tls: - disable: false - host: "0.0.0.0" - port: 7443 - status: - host: "0.0.0.0" - cron_spec: "@every 1s" - plugins: - - error-page - - real-ip - #- ai - - error-page - - client-control - - proxy-control - - zipkin - - skywalking - - ext-plugin-pre-req - - mocking - - serverless-pre-function - - batch-requests - - ua-restriction - - referer-restriction - - uri-blocker - - request-validation - - authz-casbin - - authz-casdoor - - wolf-rbac - - multi-auth - - ldap-auth - - forward-auth - - saml-auth - - opa - - authz-keycloak - #- error-log-logger - - proxy-mirror - - proxy-cache - - api-breaker - - limit-req - #- node-status - - gzip - - kafka-proxy - #- dubbo-proxy - - grpc-transcode - - grpc-web - - public-api - - data-mask - - opentelemetry - - datadog - - echo - - loggly - - splunk-hec-logging - - skywalking-logger - - google-cloud-logging - - sls-logger - - tcp-logger - - rocketmq-logger - - udp-logger - - file-logger - - clickhouse-logger - - ext-plugin-post-resp - - serverless-post-function - - azure-functions - - aws-lambda - - openwhisk - - consumer-restriction - - acl - - basic-auth - - cors - - csrf - - fault-injection - - hmac-auth - - jwt-auth - - key-auth - - openid-connect - - limit-count - - redirect - - request-id - - proxy-rewrite - - response-rewrite - - workflow - - proxy-buffering - - tencent-cloud-cls - - openfunction - - graphql-proxy-cache - - ext-plugin-post-req - #- log-rotate - - graphql-limit-count - - elasticsearch-logger - - kafka-logger - - body-transformer - - traffic-split - - degraphql - - http-logger - - cas-auth - - traffic-label - - oas-validator - - api7-traffic-split - - limit-conn - - prometheus - - syslog - - ip-restriction -dp_manager_configuration: - api_call_flush_period: 1s - server: - status: - host: "0.0.0.0" - log: - level: debug - database: - dsn: {{ .DSN }} -prometheus: - server: - persistence: - enabled: false -postgresql: -{{- if ne .DB "postgres" }} - builtin: false -{{- end }} - primary: - persistence: - enabled: false - readReplicas: - persistence: - enabled: false -developer_portal_configuration: - enable: false -dashboard_service: - type: ClusterIP - annotations: {} - port: 7080 - tlsPort: 7443 - ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: dashboard.local - paths: - - path: / - pathType: ImplementationSpecific - # backend: - # service: - # name: api7ee3-dashboard - # port: - # number: 7943 - tls: [] -api_usage: - service: - ingress: - enabled: false -`) - if err != nil { - panic(err) - } - valuesTemplate = tmpl -} - -type responseCreateGateway struct { - Value responseCreateGatewayValue `json:"value"` - ErrorMsg string `json:"error_msg"` -} - -type responseCreateGatewayValue struct { - ID string `json:"id"` - TokenPlainText string `json:"token_plain_text"` - Key string `json:"key"` -} - -func (f *Framework) deploy() { - debug := func(format string, v ...any) { - log.Infof(format, v...) - } - - kubeConfigPath := os.Getenv("KUBECONFIG") - actionConfig := new(action.Configuration) - - err := actionConfig.Init( - kube.GetConfig(kubeConfigPath, "", f.kubectlOpts.Namespace), - f.kubectlOpts.Namespace, - "memory", - debug, - ) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "init helm action config") - - install := action.NewInstall(actionConfig) - install.Namespace = f.kubectlOpts.Namespace - install.ReleaseName = "api7ee3" - - chartPath, err := install.LocateChart("api7/api7ee3", cli.New()) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "locate helm chart") - - chart, err := loader.Load(chartPath) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "load helm chart") - - buf := bytes.NewBuffer(nil) - _ = valuesTemplate.Execute(buf, map[string]any{ - "DB": _db, - "DSN": getDSN(), - "Tag": dashboardVersion, - }) - - var v map[string]any - err = yaml.Unmarshal(buf.Bytes(), &v) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "unmarshal values") - _, err = install.Run(chart, v) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "install dashboard") - - err = f.ensureService("api7ee3-dashboard", _namespace, 1) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "ensuring dashboard service") - - err = f.ensureService("api7-postgresql", _namespace, 1) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "ensuring postgres service") - - err = f.ensureService("api7-prometheus-server", _namespace, 1) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "ensuring prometheus-server service") -} - -func (f *Framework) initDashboard() { - f.deletePods("app.kubernetes.io/name=api7ee3") - time.Sleep(5 * time.Second) -} - -// ParseHTML will parse the doc from login page and generate a map contains id and action. -func (f *Framework) ParseHTML(doc *html.Node) map[string]string { - var fu func(*html.Node) - htmlMap := make(map[string]string) - fu = func(n *html.Node) { - var ( - name string - value string - ) - for _, attr := range n.Attr { - if attr.Key == "id" || attr.Key == "name" { - name = attr.Val - } - if attr.Key == "action" || attr.Key == "value" { - value = attr.Val - } - - htmlMap[name] = value - } - - for c := n.FirstChild; c != nil; c = c.NextSibling { - fu(c) - } - } - fu(doc) - - return htmlMap -} - -func (f *Framework) GetTokenFromDashboard(gatewayGroupID string) (string, error) { - respExp := f.DashboardHTTPClient(). - POST("/api/gateway_groups/"+gatewayGroupID+"/instance_token"). - WithHeader("Content-Type", "application/json"). - WithBasicAuth("admin", "admin"). - Expect() - - respExp.Status(200).Body().Contains("token_plain_text") - body := respExp.Body().Raw() - // unmarshal into responseCreateGateway - var response responseCreateGateway - err := json.Unmarshal([]byte(body), &response) - if err != nil { - return "", err - } - return response.Value.TokenPlainText, nil -} - -func (f *Framework) GetDataplaneCertificates(gatewayGroupID string) *v1.DataplaneCertificate { - respExp := f.DashboardHTTPClient(). - POST("/api/gateway_groups/"+gatewayGroupID+"/dp_client_certificates"). - WithBasicAuth("admin", "admin"). - WithHeader("Content-Type", "application/json"). - WithBytes([]byte(`{}`)). - Expect() - - f.Logger.Logf(f.GinkgoT, "dataplane certificates issuer response: %s", respExp.Body().Raw()) - - respExp.Status(200).Body().Contains("certificate").Contains("private_key").Contains("ca_certificate") - body := respExp.Body().Raw() - - var dpCertResp struct { - Value v1.DataplaneCertificate `json:"value"` - } - err := json.Unmarshal([]byte(body), &dpCertResp) - Expect(err).ToNot(HaveOccurred()) - - return &dpCertResp.Value -} - -func (s *Framework) GetAdminKey(gatewayGroupID string) string { - respExp := s.DashboardHTTPClient().PUT("/api/gateway_groups/"+gatewayGroupID+"/admin_key"). - WithHeader("Content-Type", "application/json"). - WithBasicAuth("admin", "admin"). - Expect() - - respExp.Status(200).Body().Contains("key") - - body := respExp.Body().Raw() - - var response responseCreateGateway - err := json.Unmarshal([]byte(body), &response) - Expect(err).ToNot(HaveOccurred(), "unmarshal response") - return response.Value.Key -} - -func (f *Framework) DeleteGatewayGroup(gatewayGroupID string) { - respExp := f.DashboardHTTPClient(). - DELETE("/api/gateway_groups/"+gatewayGroupID). - WithHeader("Content-Type", "application/json"). - WithBasicAuth("admin", "admin"). - Expect() - - body := respExp.Body().Raw() - - // unmarshal into responseCreateGateway - var response responseCreateGateway - err := json.Unmarshal([]byte(body), &response) - Expect(err).ToNot(HaveOccurred()) -} - -func (f *Framework) CreateNewGatewayGroupWithIngress() string { - gid, err := f.CreateNewGatewayGroupWithIngressE() - Expect(err).ToNot(HaveOccurred()) - return gid -} - -func (f *Framework) CreateNewGatewayGroupWithIngressE() (string, error) { - gatewayGroupName := uuid.NewString() - payload := []byte(fmt.Sprintf( - `{"name":"%s","description":"","labels":{},"type":"api7_ingress_controller"}`, - gatewayGroupName, - )) - - respExp := f.DashboardHTTPClient(). - POST("/api/gateway_groups"). - WithBasicAuth("admin", "admin"). - WithHeader("Content-Type", "application/json"). - WithBytes(payload). - Expect() - - f.Logger.Logf(f.GinkgoT, "create gateway group response: %s", respExp.Body().Raw()) - - respExp.Status(200).Body().Contains("id") - - body := respExp.Body().Raw() - - var response responseCreateGateway - - err := json.Unmarshal([]byte(body), &response) - if err != nil { - return "", err - } - - if response.ErrorMsg != "" { - return "", fmt.Errorf("error creating gateway group: %s", response.ErrorMsg) - } - return response.Value.ID, nil -} diff --git a/test/e2e/framework/database.go b/test/e2e/framework/database.go deleted file mode 100644 index 111df43d2..000000000 --- a/test/e2e/framework/database.go +++ /dev/null @@ -1,78 +0,0 @@ -// 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 framework - -import ( - _ "embed" - "os" - "strings" -) - -// DatabaseConfig is the database related configuration entrypoint. -type DatabaseConfig struct { - DSN string `json:"dsn" yaml:"dsn" mapstructure:"dsn"` - - MaxOpenConns int `json:"max_open_conns" yaml:"max_open_conns" mapstructure:"max_open_conns"` - MaxIdleConns int `json:"max_idle_conns" yaml:"max_idle_conns" mapstructure:"max_idle_conns"` -} - -type LogOptions struct { - // Level is the minimum logging level that a logging message should have - // to output itself. - Level string `json:"level" yaml:"level"` - // Output defines the destination file path to output logging messages. - // Two keywords "stderr" and "stdout" can be specified so that message will - // be written to stderr or stdout. - Output string `json:"output" yaml:"output"` -} - -func (conf *DatabaseConfig) GetType() string { - parts := strings.SplitN(conf.DSN, "://", 2) - if len(parts) > 1 { - return parts[0] - } - return "" -} - -var ( - _db string -) - -func init() { - _db = os.Getenv("DB") - if _db == "" { - _db = postgres - } -} - -const ( - postgres = "postgres" - oceanbase = "oceanbase" - mysql = "mysql" - postgresDSN = "postgres://api7ee:changeme@api7-postgresql:5432/api7ee" - oceanbaseDSN = "mysql://root@tcp(oceanbase:2881)/api7ee" - mysqlDSN = "mysql://root:changeme@tcp(mysql:3306)/api7ee" -) - -//nolint:unused -func getDSN() string { - switch _db { - case postgres: - return postgresDSN - case oceanbase: - return oceanbaseDSN - case mysql: - return mysqlDSN - } - panic("unknown database") -} diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go deleted file mode 100644 index 4a481987d..000000000 --- a/test/e2e/framework/framework.go +++ /dev/null @@ -1,180 +0,0 @@ -// 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 framework - -import ( - "context" - "crypto/rsa" - _ "embed" - "encoding/base64" - "fmt" - "time" - - "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/gruntwork-io/terratest/modules/logger" - . "github.com/onsi/ginkgo/v2" //nolint:staticcheck - . "github.com/onsi/gomega" //nolint:staticcheck - clientv3 "go.etcd.io/etcd/client/v3" - "gorm.io/gorm" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - _namespace = "api7-ee-e2e" - _framework *Framework -) - -type DataPlanePod struct { - Selector string - PodName string -} - -type DataPlaneContext struct { - Context context.Context - CancelFunc context.CancelFunc -} - -type Framework struct { - Context context.Context - GinkgoT GinkgoTInterface - GomegaT *GomegaWithT - - Logger logger.TestLogger - - kubectlOpts *k8s.KubectlOptions - clientset *kubernetes.Clientset - restConfig *rest.Config - K8sClient client.Client - - DB *gorm.DB - RawETCD *clientv3.Client - PrivateKey *rsa.PrivateKey - - License string - BuiltInRoles map[string]string - - Revision int64 - dpLogChan map[DataPlanePod]chan string - dpLogWatchContext map[string]*DataPlaneContext - - dashboardHTTPTunnel *k8s.Tunnel - dashboardHTTPSTunnel *k8s.Tunnel -} - -// NewFramework create a global framework with special settings. -func NewFramework() *Framework { - f := &Framework{ - GinkgoT: GinkgoT(), - GomegaT: NewWithT(GinkgoT(4)), - BuiltInRoles: make(map[string]string), - dpLogChan: make(map[DataPlanePod]chan string), - dpLogWatchContext: make(map[string]*DataPlaneContext), - Logger: logger.Terratest, - } - - // FIXME if we need some precise control on the context - f.Context = context.TODO() - - f.kubectlOpts = k8s.NewKubectlOptions("", "", _namespace) - restCfg, err := buildRestConfig("") - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "building API Server rest config") - f.restConfig = restCfg - - clientset, err := kubernetes.NewForConfig(restCfg) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "creating Kubernetes clientset") - f.clientset = clientset - - k8sClient, err := client.New(restCfg, client.Options{}) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "creating controller-runtime client") - f.K8sClient = k8sClient - - _framework = f - - // BeforeSuite(f.BeforeSuite) - // AfterSuite(f.AfterSuite) - - GinkgoWriter.Println("Another debug message") - - return f -} - -func (f *Framework) BeforeSuite() { - _ = k8s.DeleteNamespaceE(GinkgoT(), f.kubectlOpts, _namespace) - - Eventually(func() error { - _, err := k8s.GetNamespaceE(GinkgoT(), f.kubectlOpts, _namespace) - if k8serrors.IsNotFound(err) { - return nil - } - return fmt.Errorf("namespace %s still exists", _namespace) - }, "1m", "2s").Should(Succeed()) - - k8s.CreateNamespace(GinkgoT(), f.kubectlOpts, _namespace) - - f.DeployComponents() - - time.Sleep(1 * time.Minute) - err := f.newDashboardTunnel() - f.Logf("Dashboard HTTP Tunnel:" + f.dashboardHTTPTunnel.Endpoint()) - Expect(err).ShouldNot(HaveOccurred(), "creating dashboard tunnel") - - f.UploadLicense() - - f.setDpManagerEndpoints() -} - -func (f *Framework) AfterSuite() { - f.shutdownDashboardTunnel() -} - -type Items[T any] []T - -func (f *Framework) BatchDeletePublishedService(serviceIDs Items[string]) { -} -func GetFramework() *Framework { - return _framework -} - -func (f *Framework) Base64Encode(src string) string { - return base64.StdEncoding.EncodeToString([]byte(src)) -} - -// DeployComponents deploy necessary components -func (f *Framework) DeployComponents() { - f.deploy() - f.initDashboard() -} - -func (f *Framework) setDpManagerEndpoints() { - payload := []byte(fmt.Sprintf(`{"control_plane_address":["%s"]}`, DPManagerTLSEndpoint)) - - respExp := f.DashboardHTTPClient(). - PUT("/api/system_settings"). - WithBasicAuth("admin", "admin"). - WithHeader("Content-Type", "application/json"). - WithBytes(payload). - Expect() - - respExp.Raw() - f.Logf("set dp manager endpoints response: %s", respExp.Body().Raw()) - - respExp.Status(200). - Body().Contains("control_plane_address") -} - -func (f *Framework) Logf(format string, v ...any) { - f.Logger.Logf(f.GinkgoT, format, v...) -} diff --git a/test/e2e/framework/gateway.go b/test/e2e/framework/gateway.go deleted file mode 100644 index 6e4b6c975..000000000 --- a/test/e2e/framework/gateway.go +++ /dev/null @@ -1,109 +0,0 @@ -// 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 framework - -import ( - "bytes" - _ "embed" - "text/template" - - "github.com/Masterminds/sprig/v3" - "github.com/gruntwork-io/terratest/modules/k8s" - . "github.com/onsi/gomega" //nolint:staticcheck - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -var ( - //go:embed manifests/dp.yaml - _dpSpec string - DPSpecTpl *template.Template -) - -func init() { - tpl, err := template.New("dp").Funcs(sprig.TxtFuncMap()).Parse(_dpSpec) - if err != nil { - panic(err) - } - DPSpecTpl = tpl -} - -type DataPlaneDeployOptions struct { - Namespace string - Name string - - GatewayGroupID string - TLSEnabled bool - SSLKey string - SSLCert string - DPManagerEndpoint string - SetEnv bool - ForIngressGatewayGroup bool - - ServiceName string - ServiceType string - ServiceHTTPPort int - ServiceHTTPSPort int -} - -func (f *Framework) DeployGateway(opts DataPlaneDeployOptions) *corev1.Service { - if opts.ServiceName == "" { - opts.ServiceName = "api7ee3-apisix-gateway-mtls" - } - - if opts.ServiceHTTPPort == 0 { - opts.ServiceHTTPPort = 80 - } - - if opts.ServiceHTTPSPort == 0 { - opts.ServiceHTTPSPort = 443 - } - - dpCert := f.GetDataplaneCertificates(opts.GatewayGroupID) - - f.applySSLSecret(opts.Namespace, - "dp-ssl", - []byte(dpCert.Certificate), - []byte(dpCert.PrivateKey), - []byte(dpCert.CACertificate), - ) - - buf := bytes.NewBuffer(nil) - - _ = DPSpecTpl.Execute(buf, opts) - - kubectlOpts := k8s.NewKubectlOptions("", "", opts.Namespace) - - k8s.KubectlApplyFromString(f.GinkgoT, kubectlOpts, buf.String()) - - err := WaitPodsAvailable(f.GinkgoT, kubectlOpts, metav1.ListOptions{ - LabelSelector: "app.kubernetes.io/name=apisix", - }) - Expect(err).ToNot(HaveOccurred(), "waiting for gateway pod ready") - - Eventually(func() bool { - svc, err := k8s.GetServiceE(f.GinkgoT, kubectlOpts, opts.ServiceName) - if err != nil { - f.Logf("failed to get service %s: %v", opts.ServiceName, err) - return false - } - if svc.Spec.Type == corev1.ServiceTypeLoadBalancer { - return len(svc.Status.LoadBalancer.Ingress) > 0 - } - return true - }, "20s", "4s").Should(BeTrue(), "waiting for LoadBalancer IP") - - svc, err := k8s.GetServiceE(f.GinkgoT, kubectlOpts, opts.ServiceName) - Expect(err).ToNot(HaveOccurred(), "failed to get service %s: %v", opts.ServiceName, err) - return svc -} diff --git a/test/e2e/framework/ingress.go b/test/e2e/framework/ingress.go deleted file mode 100644 index 34555bb0c..000000000 --- a/test/e2e/framework/ingress.go +++ /dev/null @@ -1,67 +0,0 @@ -// 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 framework - -import ( - "bytes" - _ "embed" - "text/template" - "time" - - "github.com/Masterminds/sprig/v3" - "github.com/gruntwork-io/terratest/modules/k8s" - . "github.com/onsi/gomega" //nolint:staticcheck - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -var ( - //go:embed manifests/ingress.yaml - _ingressSpec string - IngressSpecTpl *template.Template -) - -func init() { - tpl, err := template.New("ingress").Funcs(sprig.TxtFuncMap()).Parse(_ingressSpec) - if err != nil { - panic(err) - } - IngressSpecTpl = tpl -} - -type IngressDeployOpts struct { - ControllerName string - AdminKey string - AdminTLSVerify bool - Namespace string - AdminEnpoint string - StatusAddress string - Replicas int - InitSyncDelay time.Duration -} - -func (f *Framework) DeployIngress(opts IngressDeployOpts) { - buf := bytes.NewBuffer(nil) - - err := IngressSpecTpl.Execute(buf, opts) - f.GomegaT.Expect(err).ToNot(HaveOccurred(), "rendering ingress spec") - - kubectlOpts := k8s.NewKubectlOptions("", "", opts.Namespace) - - k8s.KubectlApplyFromString(f.GinkgoT, kubectlOpts, buf.String()) - - err = WaitPodsAvailable(f.GinkgoT, kubectlOpts, metav1.ListOptions{ - LabelSelector: "control-plane=controller-manager", - }) - f.GomegaT.Expect(err).ToNot(HaveOccurred(), "waiting for controller-manager pod ready") - f.WaitControllerManagerLog("All cache synced successfully", 0, time.Minute) -} diff --git a/test/e2e/framework/k8s.go b/test/e2e/framework/k8s.go deleted file mode 100644 index dc06f310e..000000000 --- a/test/e2e/framework/k8s.go +++ /dev/null @@ -1,354 +0,0 @@ -// 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 framework - -import ( - "context" - "fmt" - "net/http" - "net/url" - "time" - - "github.com/api7/gopkg/pkg/log" - "github.com/gavv/httpexpect" - "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/gruntwork-io/terratest/modules/testing" - . "github.com/onsi/gomega" //nolint:staticcheck - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - applycorev1 "k8s.io/client-go/applyconfigurations/core/v1" - applymetav1 "k8s.io/client-go/applyconfigurations/meta/v1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/utils/ptr" -) - -// buildRestConfig builds the rest.Config object from kubeconfig filepath and -// context, if kubeconfig is missing, building from in-cluster configuration. -func buildRestConfig(context string) (*rest.Config, error) { - - // Config loading rules: - // 1. kubeconfig if it not empty string - // 2. Config(s) in KUBECONFIG environment variable - // 3. In cluster config if running in-cluster - // 4. Use $HOME/.kube/config - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig - configOverrides := &clientcmd.ConfigOverrides{ - ClusterDefaults: clientcmd.ClusterDefaults, - CurrentContext: context, - } - - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - return clientConfig.ClientConfig() -} - -func (f *Framework) ensureService(name, namespace string, desiredEndpoints int) error { - return f.ensureServiceWithTimeout(name, namespace, desiredEndpoints, 120) -} - -func (f *Framework) ensureServiceWithTimeout(name, namespace string, desiredEndpoints, timeout int) error { - backoff := wait.Backoff{ - Duration: 6 * time.Second, - Factor: 1, - Steps: timeout / 6, - } - var lastErr error - condFunc := func() (bool, error) { - ep, err := f.clientset.CoreV1().Endpoints(namespace).Get(f.Context, name, metav1.GetOptions{}) - if err != nil { - lastErr = err - log.Errorw("failed to list endpoints", - zap.String("service", name), - zap.Error(err), - ) - return false, nil - } - count := 0 - for _, ss := range ep.Subsets { - count += len(ss.Addresses) - } - if count == desiredEndpoints { - return true, nil - } - log.Infow("endpoints count mismatch", - zap.String("service", name), - zap.Any("ep", ep), - zap.Int("expected", desiredEndpoints), - zap.Int("actual", count), - ) - lastErr = fmt.Errorf("expected endpoints: %d but seen %d", desiredEndpoints, count) - return false, nil - } - - err := wait.ExponentialBackoff(backoff, condFunc) - if err != nil { - return lastErr - } - return nil -} - -func (f *Framework) GetServiceEndpoints(name string) ([]string, error) { - ep, err := f.clientset.CoreV1().Endpoints(_namespace).Get(f.Context, name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - var endpoints []string - for _, ss := range ep.Subsets { - for _, addr := range ss.Addresses { - endpoints = append(endpoints, addr.IP) - } - } - return endpoints, nil -} - -//nolint:unused -func (f *Framework) deletePods(selector string) { - podList, err := f.clientset.CoreV1().Pods(_namespace).List(f.Context, metav1.ListOptions{ - LabelSelector: selector, - }) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "list pods") - for _, pod := range podList.Items { - _ = f.clientset.CoreV1(). - Pods(_namespace). - Delete(f.Context, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: ptr.To(int64(30))}) - } -} - -func (f *Framework) CreateNamespaceWithTestService(name string) { - _, err := f.clientset.CoreV1(). - Namespaces(). - Create(f.Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{}) - if err != nil && !errors.IsAlreadyExists(err) { - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "create namespace") - return - } - - _, err = f.clientset.CoreV1().Services(name).Create(f.Context, &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: name, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "http", - Port: 80, - Protocol: corev1.ProtocolTCP, - }, - }, - Selector: map[string]string{ - "app": "httpbin", - }, - Type: corev1.ServiceTypeClusterIP, - }, - }, metav1.CreateOptions{}) - if err != nil && !errors.IsAlreadyExists(err) { - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "create service") - } -} - -func (f *Framework) DeleteNamespace(name string) { - err := f.clientset.CoreV1().Namespaces().Delete(f.Context, name, metav1.DeleteOptions{}) - if err == nil || errors.IsNotFound(err) { - return - } - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "delete namespace") -} - -func (f *Framework) Scale(name string, replicas int32) { - scale, err := f.clientset.AppsV1().Deployments(_namespace).GetScale(context.Background(), name, metav1.GetOptions{}) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("get deployment %s scale failed", name)) - if scale.Spec.Replicas == replicas { - return - } - scale.Spec.Replicas = replicas - _, err = f.clientset.AppsV1(). - Deployments(_namespace). - UpdateScale(context.Background(), name, scale, metav1.UpdateOptions{}) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("scale deployment %s to %v failed", name, replicas)) - - err = f.ensureService(name, _namespace, int(replicas)) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), - fmt.Sprintf("ensure service %s/%s has %v endpoints failed", _namespace, name, replicas)) -} - -func (f *Framework) GetPodIP(selector string) string { - podList, err := f.clientset.CoreV1().Pods(_namespace).List(f.Context, metav1.ListOptions{ - LabelSelector: selector, - }) - f.GomegaT.Expect(err).ShouldNot(HaveOccurred()) - f.GomegaT.Expect(podList.Items).ShouldNot(BeEmpty()) - return podList.Items[0].Status.PodIP -} - -func (f *Framework) newDashboardTunnel() error { - var ( - httpNodePort int - httpsNodePort int - httpPort int - httpsPort int - ) - - service := k8s.GetService(f.GinkgoT, f.kubectlOpts, "api7ee3-dashboard") - - for _, port := range service.Spec.Ports { - switch port.Name { - case "http": - httpNodePort = int(port.NodePort) - httpPort = int(port.Port) - case "https": - httpsNodePort = int(port.NodePort) - httpsPort = int(port.Port) - } - } - - f.dashboardHTTPTunnel = k8s.NewTunnel(f.kubectlOpts, k8s.ResourceTypeService, "api7ee3-dashboard", - httpNodePort, httpPort) - f.dashboardHTTPSTunnel = k8s.NewTunnel(f.kubectlOpts, k8s.ResourceTypeService, "api7ee3-dashboard", - httpsNodePort, httpsPort) - - if err := f.dashboardHTTPTunnel.ForwardPortE(f.GinkgoT); err != nil { - return err - } - if err := f.dashboardHTTPSTunnel.ForwardPortE(f.GinkgoT); err != nil { - return err - } - - return nil -} - -func (f *Framework) shutdownDashboardTunnel() { - if f.dashboardHTTPTunnel != nil { - f.dashboardHTTPTunnel.Close() - } - if f.dashboardHTTPSTunnel != nil { - f.dashboardHTTPSTunnel.Close() - } -} - -func (f *Framework) GetDashboardEndpoint() string { - return f.dashboardHTTPTunnel.Endpoint() -} - -func (f *Framework) GetDashboardEndpointHTTPS() string { - return f.dashboardHTTPSTunnel.Endpoint() -} - -func (f *Framework) DashboardHTTPClient() *httpexpect.Expect { - u := url.URL{ - Scheme: "http", - Host: f.GetDashboardEndpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{}, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(f.GinkgoT), - ), - }) -} - -func (f *Framework) DashboardHTTPSClient() *httpexpect.Expect { - u := url.URL{ - Scheme: "https", - Host: f.GetDashboardEndpointHTTPS(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{}, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(f.GinkgoT), - ), - }) -} - -func (f *Framework) applySSLSecret(namespace, name string, cert, pkey, caCert []byte) { - kind := "Secret" - apiVersion := "v1" - secretType := corev1.SecretTypeTLS - secret := applycorev1.SecretApplyConfiguration{ - TypeMetaApplyConfiguration: applymetav1.TypeMetaApplyConfiguration{ - Kind: &kind, - APIVersion: &apiVersion, - }, - ObjectMetaApplyConfiguration: &applymetav1.ObjectMetaApplyConfiguration{ - Name: &name, - }, - Data: map[string][]byte{ - "tls.crt": cert, - "tls.key": pkey, - "ca.crt": caCert, - }, - Type: &secretType, - } - - cli, err := k8s.GetKubernetesClientE(f.GinkgoT) - Expect(err).ToNot(HaveOccurred()) - - _, err = cli.CoreV1().Secrets(namespace).Apply(context.TODO(), &secret, metav1.ApplyOptions{ - FieldManager: "e2e", - }) - Expect(err).ToNot(HaveOccurred(), "apply secret") -} - -func WaitPodsAvailable(t testing.TestingT, kubeOps *k8s.KubectlOptions, opts metav1.ListOptions) error { - condFunc := func() (bool, error) { - items, err := k8s.ListPodsE(t, kubeOps, opts) - if err != nil { - return false, err - } - if len(items) == 0 { - return false, nil - } - for _, item := range items { - foundPodReady := false - for _, cond := range item.Status.Conditions { - if cond.Type != corev1.PodReady { - continue - } - foundPodReady = true - if cond.Status != "True" { - return false, nil - } - } - if !foundPodReady { - return false, nil - } - } - return true, nil - } - return waitExponentialBackoff(condFunc) -} - -func waitExponentialBackoff(condFunc func() (bool, error)) error { - backoff := wait.Backoff{ - Duration: 500 * time.Millisecond, - Factor: 2, - Steps: 8, - } - return wait.ExponentialBackoff(backoff, condFunc) -} diff --git a/test/e2e/framework/license.go b/test/e2e/framework/license.go deleted file mode 100644 index 1ade6c6e7..000000000 --- a/test/e2e/framework/license.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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 framework - -import ( - _ "embed" - "encoding/json" - - "github.com/stretchr/testify/assert" -) - -func (f *Framework) UploadLicense() { - payload := map[string]any{"data": API7EELicense} - payloadBytes, err := json.Marshal(payload) - assert.Nil(f.GinkgoT, err) - - respExpect := f.DashboardHTTPClient().PUT("/api/license"). - WithBasicAuth("admin", "admin"). - WithHeader("Content-Type", "application/json"). - WithBytes(payloadBytes). - Expect() - - body := respExpect.Body().Raw() - f.Logf("request /api/license, response body: %s", body) - - respExpect.Status(200) -} diff --git a/test/e2e/framework/manifests/cert.pem b/test/e2e/framework/manifests/cert.pem deleted file mode 100644 index 2dbe89ddf..000000000 --- a/test/e2e/framework/manifests/cert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIJALDqPppBVXQ3MA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNV -BAYTAkNOMRAwDgYDVQQIDAdKaWFuZ3N1MQ8wDQYDVQQHDAZTdXpob3UxEDAOBgNV -BAoMB2FwaTcuYWkxEDAOBgNVBAsMB2FwaTcuYWkxDzANBgNVBAMMBmp3LmNvbTAg -Fw0yMTA0MDkwNzEyMDBaGA8yMDUxMDQwMjA3MTIwMFowZTELMAkGA1UEBhMCQ04x -EDAOBgNVBAgMB0ppYW5nc3UxDzANBgNVBAcMBlN1emhvdTEQMA4GA1UECgwHYXBp -Ny5haTEQMA4GA1UECwwHYXBpNy5haTEPMA0GA1UEAwwGancuY29tMIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuEPPnUMlSw41CTdxUNxkQ4gAZ7cPotwY -Y6sVLGtWoR8gKFSZImQIor3UF+8HhN/ZOFRv5tSeeQ/MTE72cs2T5mp6eRU8OrSV -0npk/TpZfaCx7zobsfXB4YU1NZcVWKthFF5X8p//l5gxUMU8V4a01P0aDTmZ67wG -3Fhr0AC067GVYvdwp1yRt6TUUk8ha7JsiySchUIFhX5QMWmrSNhc1bDnHetejMFl -itFFPZkeYG89O/7Ca1K3ca/VVu+/IJ4h7fbF3rt4uP182cJdHl1L94dQSKCJukaW -v+xauWnm4hxOzBK7ImpYB/2eP2D33tmuCLeSv4S+bTG1l7hIN9C/xrYPzfun415h -M2jMK69aAkQL71xa+66kVxioJyNYogYz3ss5ruzDL8K/7bkdO0Zzqldd2+j8lkTl -X4csA+aMHF3v/U7hL/4Wdwi8ziwToRMq9KK9vuh+mPgcdtFGFml+sU+NQfJNm/BN -7fRMZKDIHLacSPE0GUkfW+x3dXOP2lWSZe/iOBZ0NOGNdrOnxTRTr7IH7DYU8aXF -w2GqfAFEQbD4wazCh1AI8lkZr6mPGB1q+HnF2IF7kkgXBHtI5U2KErgcX5BirIVe -v0Yg/OxbbymeTh/hNCcY1kJ1YUCbm9U3U6ZV+d8lj7dQHtugcAzWxSTwpBLVUPrO -eFuhSMLVblUCAwEAAaMjMCEwHwYDVR0RBBgwFoIIYXBpNi5jb22CCiouYXBpNi5j -b20wDQYJKoZIhvcNAQELBQADggIBAFgeSuMPpriYUrQByO3taiY1+56s+KoAmsyA -LH15n2+thAgorusX2g1Zd7UNBHBtVO8c+ihpQb+2PnmgTTGT70ndpRbV5F6124Mt -Hui/X0kjm76RYd1QKN1VFp0Zo+JVdRa+VhDsXWjO0VetINmFhNINFEJctyeHB8oi -aaDL0wZrevHh47hBqtnrmLl+QVG34aLBRhZ5953leiNvXHUJNaT0nLgf0j9p4esS -b2bx9uP4pFI1T9wcv/TE3K0rQbu/uqGY6MgznXHyi4qIK/I+WCa3fF2UZ5P/5EUM -k2ptQneYkLLUVwwmj8C04bYhYe7Z6jkYYp17ojxIP+ejOY1eiE8SYKNjKinmphrM -5aceqIyfbE4TPqvicNzIggA4yEMPbTA0PC/wqbCf61oMc15hwacdeIaQGiwsM+pf -DTk+GBxp3megx/+0XwTQbguleTlHnaaES+ma0vbl6a1rUK0YAUDUrcfFLv6EFtGD -6EHxFf7gH9sTfc2RiGhKxUhRbyEree+6INAvXy+AymVYmQmKuAFqkDQJ+09bTfm8 -bDs+00FijzHFBvC8cIhNffj0qqiv35g+9FTwnE9qpunlrtKG/sMgEXX6m8kL1YQ8 -m5DPGhyEZyt5Js2kzzo8TyINPKmrqriYuiD4p4EH13eSRs3ayanQh6ckC7lb+WXq -3IrSc5hO ------END CERTIFICATE----- diff --git a/test/e2e/framework/manifests/dp.yaml b/test/e2e/framework/manifests/dp.yaml deleted file mode 100644 index 0924e1ac1..000000000 --- a/test/e2e/framework/manifests/dp.yaml +++ /dev/null @@ -1,267 +0,0 @@ -apiVersion: v1 -data: - config.yaml: |- - api7ee: - healthcheck_report_interval: 1 - apisix: - node_listen: - - 9080 - - enable_http2: true - port: 9081 - enable_admin: true - ssl: - enabled: true - {{- if .TLSEnabled }} - ssl_trusted_certificate: /opts/etcd/ca.crt - {{- end }} - stream_proxy: - tcp: - - 9100 - nginx_config: - worker_processes: 2 - error_log_level: debug - deployment: - role: traditional - role_traditional: - config_provider: etcd - etcd: - host: - - "{{ .DPManagerEndpoint }}" - timeout: 30 - resync_delay: 0 - {{- if .TLSEnabled }} - tls: - verify: true - cert: /opts/etcd/tls.crt - key: /opts/etcd/tls.key - {{- end }} - admin: - allow_admin: - - all - plugins: - - error-page - - real-ip - - ai - - client-control - - proxy-buffering - - proxy-control - - request-id - - zipkin - - skywalking - - opentelemetry - - ext-plugin-pre-req - - fault-injection - - mocking - - serverless-pre-function - - cors - - ip-restriction - - ua-restriction - - referer-restriction - - csrf - - uri-blocker - - request-validation - - openid-connect - - saml-auth - - cas-auth - - authz-casbin - - authz-casdoor - - wolf-rbac - - ldap-auth - - hmac-auth - - basic-auth - - jwt-auth - - key-auth - - multi-auth - - acl - - consumer-restriction - - forward-auth - - opa - - authz-keycloak - - data-mask - - proxy-cache - - graphql-proxy-cache - - body-transformer - - proxy-mirror - - proxy-rewrite - - workflow - - api-breaker - - graphql-limit-count - - limit-conn - - limit-count - - limit-req - - traffic-label - - gzip - - server-info - - api7-traffic-split - - traffic-split - - redirect - - response-rewrite - - oas-validator - - degraphql - - kafka-proxy - - grpc-transcode - - grpc-web - - public-api - - prometheus - - datadog - - elasticsearch-logger - - echo - - loggly - - http-logger - - splunk-hec-logging - - skywalking-logger - - google-cloud-logging - - sls-logger - - tcp-logger - - kafka-logger - - rocketmq-logger - - syslog - - udp-logger - - file-logger - - clickhouse-logger - - tencent-cloud-cls - - example-plugin - - aws-lambda - - azure-functions - - openwhisk - - openfunction - - serverless-post-function - - ext-plugin-post-req - - ext-plugin-post-resp - -kind: ConfigMap -metadata: - name: api7ee3-apisix{{- if .TLSEnabled }}-mtls{{- end }} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: api7ee3 - app.kubernetes.io/name: apisix - name: api7ee3-apisix{{- if .TLSEnabled }}-mtls{{- end }} -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/instance: api7ee3 - app.kubernetes.io/name: apisix - {{- if .TLSEnabled }} - cp-connection: mtls - {{- end }} - template: - metadata: - labels: - app.kubernetes.io/instance: api7ee3 - app.kubernetes.io/name: apisix - {{- if .TLSEnabled }} - cp-connection: mtls - {{- end }} - spec: - #serviceAccountName: ginkgo - containers: - - image: hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev - imagePullPolicy: IfNotPresent - env: - {{- if not .TLSEnabled }} - - name: API7_CONTROL_PLANE_TOKEN - value: "{{ .ControlPlaneToken }}" - {{else}} - - name: API7_CONTROL_PLANE_TOKEN - value: "a7ee-placeholder" - {{- end }} - {{- if .SetEnv }} - - name: JACK_AUTH_KEY - value: auth-one - - name: SSL_CERT - value: | - {{- .SSLCert | nindent 12 }} - - name: SSL_KEY - value: | - {{- .SSLKey | nindent 12 }} - {{- end }} - name: apisix - ports: - - containerPort: 9080 - name: http - protocol: TCP - - containerPort: 9081 - name: http2 - protocol: TCP - - containerPort: 9180 - name: admin - protocol: TCP - - containerPort: 9443 - name: tls - protocol: TCP - - containerPort: 9090 - name: control-api - protocol: TCP - - containerPort: 9100 - name: stream-route - protocol: TCP - readinessProbe: - failureThreshold: 10 - initialDelaySeconds: 3 - periodSeconds: 3 - successThreshold: 1 - tcpSocket: - port: 9080 - timeoutSeconds: 1 - volumeMounts: - - mountPath: /usr/local/apisix/conf/config.yaml - name: apisix-config - subPath: config.yaml - {{- if .TLSEnabled }} - - mountPath: /opts/etcd - name: dp-ssl - {{- end }} - securityContext: - runAsNonRoot: false - runAsUser: 0 - dnsPolicy: ClusterFirst - volumes: - - configMap: - defaultMode: 420 - name: api7ee3-apisix{{- if .TLSEnabled }}-mtls{{- end }} - name: apisix-config - {{- if .TLSEnabled }} - - secret: - secretName: dp-ssl - name: dp-ssl - {{- end }} ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/instance: api7ee3 - app.kubernetes.io/name: apisix - app.kubernetes.io/service: apisix-gateway - name: {{ .ServiceName }} -spec: - ports: - - name: http - port: {{ .ServiceHTTPPort }} - protocol: TCP - targetPort: 9080 - - name: http2 - port: 9081 - protocol: TCP - targetPort: 9081 - - name: https - port: {{ .ServiceHTTPSPort }} - protocol: TCP - targetPort: 9443 - - name: control-api - port: 9090 - protocol: TCP - targetPort: 9090 - - name: tcp - port: 9100 - protocol: TCP - selector: - app.kubernetes.io/instance: api7ee3 - app.kubernetes.io/name: apisix - cp-connection: mtls - type: {{ .ServiceType | default "NodePort" }} diff --git a/test/e2e/framework/manifests/ingress.yaml b/test/e2e/framework/manifests/ingress.yaml deleted file mode 100644 index 4aeabb893..000000000 --- a/test/e2e/framework/manifests/ingress.yaml +++ /dev/null @@ -1,451 +0,0 @@ - - -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: apisix-ingress - name: apisix-ingress-controller-manager - namespace: {{ .Namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: apisix-ingress - name: apisix-ingress-leader-election-role - namespace: {{ .Namespace }} -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: apisix-ingress-manager-role -rules: -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - get - - list - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - get - - list - - watch -- apiGroups: - - apisix.apache.org - resources: - - backendtrafficpolicies - verbs: - - get - - list - - watch -- apiGroups: - - apisix.apache.org - resources: - - backendtrafficpolicies/status - verbs: - - get - - update -- apiGroups: - - apisix.apache.org - resources: - - consumers - verbs: - - get - - list - - watch -- apiGroups: - - apisix.apache.org - resources: - - consumers/status - verbs: - - get - - update -- apiGroups: - - apisix.apache.org - resources: - - gatewayproxies - verbs: - - get - - list - - watch -- apiGroups: - - apisix.apache.org - resources: - - httproutepolicies - verbs: - - get - - list - - watch -- apiGroups: - - apisix.apache.org - resources: - - httproutepolicies/status - verbs: - - get - - update -- apiGroups: - - apisix.apache.org - resources: - - httproutepolicies - verbs: - - get - - list - - watch -- apiGroups: - - apisix.apache.org - resources: - - httproutepolicies/status - verbs: - - get - - update -- apiGroups: - - apisix.apache.org - resources: - - pluginconfigs - verbs: - - get - - list - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses - verbs: - - get - - list - - update - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - gatewayclasses/status - verbs: - - get - - update -- apiGroups: - - gateway.networking.k8s.io - resources: - - gateways - verbs: - - get - - list - - update - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - gateways/status - verbs: - - get - - update -- apiGroups: - - gateway.networking.k8s.io - resources: - - httproutes - verbs: - - get - - list - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - httproutes/status - verbs: - - get - - update -- apiGroups: - - gateway.networking.k8s.io - resources: - - referencegrants - verbs: - - get - - list - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - referencegrants/status - verbs: - - get -- apiGroups: - - networking.k8s.io - resources: - - ingressclasses - verbs: - - get - - list - - watch -- apiGroups: - - networking.k8s.io - resources: - - ingresses - verbs: - - get - - list - - update - - watch -- apiGroups: - - networking.k8s.io - resources: - - ingresses/status - verbs: - - get - - update ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: apisix-ingress-metrics-auth-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: apisix-ingress-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: apisix-ingress - name: apisix-ingress-leader-election-rolebinding - namespace: {{ .Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: apisix-ingress-leader-election-role -subjects: -- kind: ServiceAccount - name: apisix-ingress-controller-manager - namespace: {{ .Namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: apisix-ingress - name: apisix-ingress-manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: apisix-ingress-manager-role -subjects: -- kind: ServiceAccount - name: apisix-ingress-controller-manager - namespace: {{ .Namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: apisix-ingress-metrics-auth-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: apisix-ingress-metrics-auth-role -subjects: -- kind: ServiceAccount - name: apisix-ingress-controller-manager - namespace: {{ .Namespace }} ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: ingress-config -data: - config.yaml: | - log_level: "debug" - - controller_name: {{ .ControllerName | default "apisix.apache.org/apisix-ingress-controller" }} - - leader_election_id: "apisix-ingress-controller-leader" - provider: - sync_period: 0s - # The period between two consecutive syncs. - # The default value is 0 seconds, which means the controller will not sync. - # If you want to enable the sync, set it to a positive value. - init_sync_delay: {{ .InitSyncDelay | default "1m" }} ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: apisix-ingress - control-plane: controller-manager - name: apisix-ingress-controller-manager-metrics-service - namespace: {{ .Namespace }} -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: 8443 - selector: - control-plane: controller-manager ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: apisix-ingress - app: apisix-ingress-controller - control-plane: controller-manager - name: apisix-ingress-controller-manager - namespace: {{ .Namespace }} -spec: - replicas: {{ .Replicas | default 1 }} - selector: - matchLabels: - app: apisix-ingress-controller - control-plane: controller-manager - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: manager - labels: - app: apisix-ingress-controller - control-plane: controller-manager - spec: - containers: - - image: api7/api7-ingress-controller:dev - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - volumeMounts: - - name: ingress-config - mountPath: /app/conf/config.yaml - subPath: config.yaml - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - volumes: - - name: ingress-config - configMap: - name: ingress-config - serviceAccountName: apisix-ingress-controller-manager - terminationGracePeriodSeconds: 10 diff --git a/test/e2e/framework/manifests/key.pem b/test/e2e/framework/manifests/key.pem deleted file mode 100644 index b70cfbbf5..000000000 --- a/test/e2e/framework/manifests/key.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAuEPPnUMlSw41CTdxUNxkQ4gAZ7cPotwYY6sVLGtWoR8gKFSZ -ImQIor3UF+8HhN/ZOFRv5tSeeQ/MTE72cs2T5mp6eRU8OrSV0npk/TpZfaCx7zob -sfXB4YU1NZcVWKthFF5X8p//l5gxUMU8V4a01P0aDTmZ67wG3Fhr0AC067GVYvdw -p1yRt6TUUk8ha7JsiySchUIFhX5QMWmrSNhc1bDnHetejMFlitFFPZkeYG89O/7C -a1K3ca/VVu+/IJ4h7fbF3rt4uP182cJdHl1L94dQSKCJukaWv+xauWnm4hxOzBK7 -ImpYB/2eP2D33tmuCLeSv4S+bTG1l7hIN9C/xrYPzfun415hM2jMK69aAkQL71xa -+66kVxioJyNYogYz3ss5ruzDL8K/7bkdO0Zzqldd2+j8lkTlX4csA+aMHF3v/U7h -L/4Wdwi8ziwToRMq9KK9vuh+mPgcdtFGFml+sU+NQfJNm/BN7fRMZKDIHLacSPE0 -GUkfW+x3dXOP2lWSZe/iOBZ0NOGNdrOnxTRTr7IH7DYU8aXFw2GqfAFEQbD4wazC -h1AI8lkZr6mPGB1q+HnF2IF7kkgXBHtI5U2KErgcX5BirIVev0Yg/OxbbymeTh/h -NCcY1kJ1YUCbm9U3U6ZV+d8lj7dQHtugcAzWxSTwpBLVUPrOeFuhSMLVblUCAwEA -AQKCAgApTupoMvlVTiYNnuREYGQJz59noN5cgELndR8WCiotjLDE2dJKp2pYMX4u -r2NcImKsAiHj+Z5dPXFrWfhd3EBf01cJdf0+m+VKfi3NpxsQ0smQ+9Hhn1qLmDVJ -gklCy4jD7DKDLeM6tN+5X74bUROQ+/yvIk6jTk+rbhcdVks422LGAPq8SkBQjx8a -JKs1XZZ/ywFbzmU2fA62RR4lAnwtW680QeO8Yk7FRAzltkHdFJMBtCcZsD13uxd0 -meKbCVhJ5JyPRi/WKN2oY65EdF3na+pPnc3CeLiq5e2gy2D7J6VyknBpUrXRdMXZ -J3/p8ZrWUXEQhk26ZP50uNdXy/Bx1jYe+U8mpkTMYVYxgu5K4Zea3yJyRn2piiE/ -9LnKNy/KsINt/0QE55ldvtciyP8RDA/08eQX0gvtKWWC/UFVRZCeL48bpqLmdAfE -cMwlk1b0Lmo2PxULFLMAjaTKmcMAbwl53YRit0MtvaIOwiZBUVHE0blRiKC2DMKi -SA6xLbaYJVDaMcfix8kZkKbC0xBNmL4123qX4RF6IUTPufyUTz/tpjzH6eKDw/88 -LmSx227d7/qWp5gROCDhZOPhtr4rj70JKNqcXUX9iFga+dhloOwfHYjdKndKOLUI -Gp3K9YkPT/fCfesrguUx8BoleO5pC6RQJhRsemkRGlSY8U1ZsQKCAQEA5FepCn1f -A46GsBSQ+/pbaHlbsR2syN3J5RmAFLFozYUrqyHE/cbNUlaYq397Ax7xwQkiN3F2 -z/whTxOh4Sk/HdDF4d+I0PZeoxINxgfzyYkx8Xpzn2loxsRO8fb3g+mOxZidjjXv -vxqUBaj3Y01Ig0UXuw7YqCwys+xg3ELtvcGLBW/7NWMo8zqk2nUjhfcwp4e4AwBt -Xcsc2aShUlr/RUrJH4adjha6Yaqc/8xTXHW8gZi5L2lucwB0LA+CBe4ES9BZLZdX -N575/ohXRdjadHKYceYHiamt2326DzaxVJu2EIXU8dgdgOZ/6krITzuePRQHLPHX -6bDfdg/WSpFrtwKCAQEAzpVqBcJ1fAI7bOkt89j2zZb1r5uD2f9sp/lA/Dj65QKV -ShWR7Y6Jr4ShXmFvIenWtjwsl86PflMgtsJefOmLyv8o7PL154XD8SnNbBlds6IM -MyNKkOJFa5NOrsal7TitaTvtYdKq8Zpqtxe+2kg80wi+tPVQNQS/drOpR3rDiLIE -La/ty8XDYZsSowlzBX+uoFq7GuMct1Uh2T0/I4Kf0ZLXwYjkRlRk4LrU0BRPhRMu -MHugOTYFKXShE2a3OEcxqCgvQ/3pn2TV92pPVKBIBGL6uKUwmXQYKaV3G4u10pJ4 -axq/avBOErcKZOReb0SNiOjiIsth8o+hnpYPU5COUwKCAQBksVNV0NtpUhyK4Ube -FxTgCUQp4pAjM8qoQIp+lY1FtAgBuy6HSneYa59/YQP56FdrbH+uO1bNeL2nhVzJ -UcsHdt0MMeq/WyV4e6mfPjp/EQT5G6qJDY6quD6n7ORRQ1k2QYqY/6fteeb0aAJP -w/DKElnYnz9jSbpCJWbBOrJkD0ki6LK6ZDPWrnGr9CPqG4tVFUBL8pBH4B2kzDhn -fME86TGvuUkZM2SVVQtOsefAyhqKe7KN+cw+4mBYXa5UtxUl6Yap2CcZ2/0aBT2X -C32qBC69a1a/mheUxuiZdODWEqRCvQGedFLuWLbntnqGlh+9h2tyomM4JkskYO96 -io4ZAoIBAFouLW9AOUseKlTb4dx+DRcoXC4BpGhIsWUOUQkJ0rSgEQ2bJu3d+Erv -igYKYJocW0eIMys917Qck75UUS0UQpsmEfaGBUTBRw0C45LZ6+abydmVAVsH+6f/ -USzIuOw6frDeoTy/2zHG5+jva7gcKrkxKxcRs6bBYNdvjGkQtUT5+Qr8rsDyntz/ -9f3IBTcUSuXjVaRiGkoJ1tHfg617u0qgYKEyofv1oWfdB0Oiaig8fEBb51CyPUSg -jiRLBZaCtbGjgSacNB0JxsHP3buikG2hy7NJIVMLs/SSL9GNhpzapciTj5YeOua+ -ksICUxsdgO+QQg9QW3yoqLPy69Pd2dMCggEBANDLoUf3ZE7Dews6cfIa0WC33NCV -FkyECaF2MNOp5Q9y/T35FyeA7UeDsTZ6Dy++XGW4uNStrSI6dCyQARqJw+i7gCst -2m5lIde01ptzDQ9iO1Dv1XasxX/589CyLq6CxLfRgPMJPDeUEg0X7+a0lBT5Hpwk -gNnZmws4l3i7RlVMtACCenmof9VtOcMK/9Qr502WHEoGkQR1r6HZFb25841cehL2 -do+oXlr8db++r87a8QQUkizzc6wXD9JffBNo9AO9Ed4HVOukpEA0gqVGBu85N3xW -jW4KB95bGOTa7r7DM1Up0MbAIwWoeLBGhOIXk7inurZGg+FNjZMA5Lzm6qo= ------END RSA PRIVATE KEY----- diff --git a/test/e2e/framework/manifests/nginx.yaml b/test/e2e/framework/manifests/nginx.yaml deleted file mode 100644 index f9a1dbc04..000000000 --- a/test/e2e/framework/manifests/nginx.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: nginx-config -data: - nginx.conf: | - worker_processes 1; - pid /run/nginx.pid; - - events { - worker_connections 1024; - } - - http { - server { - listen 80 default_server; - - location / { - return 200 'Hello, World!'; - } - } - } - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx -spec: - replicas: 1 - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - spec: - volumes: - - name: nginx-config - configMap: - name: nginx-config - containers: - - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 1 - periodSeconds: 5 - successThreshold: 1 - httpGet: - path: /healthz - port: 80 - timeoutSeconds: 2 - image: "nginx:1.21.4" - imagePullPolicy: IfNotPresent - name: nginx - ports: - - containerPort: 80 - name: "http" - protocol: "TCP" - volumeMounts: - - mountPath: /etc/nginx/nginx.conf - name: nginx-config - subPath: nginx.conf ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx -spec: - selector: - app: nginx - ports: - - name: http - port: 80 - protocol: TCP - targetPort: 80 - type: ClusterIP diff --git a/test/e2e/framework/nginx.go b/test/e2e/framework/nginx.go deleted file mode 100644 index cf5044e7a..000000000 --- a/test/e2e/framework/nginx.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 framework - -import ( - "bytes" - _ "embed" - "text/template" - - "github.com/Masterminds/sprig/v3" - "github.com/gruntwork-io/terratest/modules/k8s" - . "github.com/onsi/gomega" //nolint:staticcheck - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -var ( - //go:embed manifests/nginx.yaml - _ngxSpec string - ngxSpecTpl *template.Template -) - -type NginxOptions struct { - Namespace string -} - -func init() { - tpl, err := template.New("ngx").Funcs(sprig.TxtFuncMap()).Parse(_ngxSpec) - if err != nil { - panic(err) - } - ngxSpecTpl = tpl -} - -func (f *Framework) DeployNginx(opts NginxOptions) *corev1.Service { - buf := bytes.NewBuffer(nil) - - err := ngxSpecTpl.Execute(buf, opts) - f.GomegaT.Expect(err).ToNot(HaveOccurred(), "rendering nginx spec") - - kubectlOpts := k8s.NewKubectlOptions("", "", opts.Namespace) - - k8s.KubectlApplyFromString(f.GinkgoT, kubectlOpts, buf.String()) - - err = WaitPodsAvailable(f.GinkgoT, kubectlOpts, metav1.ListOptions{ - LabelSelector: "app=nginx", - }) - Expect(err).ToNot(HaveOccurred(), "waiting for nginx pod ready") - - return k8s.GetService(f.GinkgoT, kubectlOpts, "nginx") -} diff --git a/test/e2e/framework/utils.go b/test/e2e/framework/utils.go deleted file mode 100644 index 511fd94fc..000000000 --- a/test/e2e/framework/utils.go +++ /dev/null @@ -1,444 +0,0 @@ -// 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 framework - -import ( - "archive/zip" - "bufio" - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "net/url" - "slices" - "strings" - "sync" - "time" - - "github.com/gavv/httpexpect/v2" - "github.com/gruntwork-io/terratest/modules/testing" - "github.com/onsi/gomega" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - "golang.org/x/net/html" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/remotecommand" - "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" - - "github.com/apache/apisix-ingress-controller/api/v1alpha1" -) - -func (f *Framework) NewExpectResponse(httpBody any) *httpexpect.Response { - body, err := json.Marshal(httpBody) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred()) - - return httpexpect.NewResponse(f.GinkgoT, &http.Response{ - Header: http.Header{ - "Content-Type": []string{"application/json"}, - }, - Body: io.NopCloser(bytes.NewBuffer(body)), - }) -} - -// ListPods query pods by label selector. -func (f *Framework) ListPods(selector string) []corev1.Pod { - pods, err := f.clientset.CoreV1().Pods(_namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: selector, - }) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "list pod: ", selector) - return pods.Items -} - -func (f *Framework) ListRunningPods(selector string) []corev1.Pod { - pods, err := f.clientset.CoreV1().Pods(_namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: selector, - }) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "list pod: ", selector) - runningPods := make([]corev1.Pod, 0) - for _, p := range pods.Items { - if p.Status.Phase == corev1.PodRunning && p.DeletionTimestamp == nil { - runningPods = append(runningPods, p) - } - } - return runningPods -} - -// ExecCommandInPod exec cmd in specify pod and return the output from stdout and stderr -func (f *Framework) ExecCommandInPod(podName string, cmd ...string) (string, string) { - req := f.clientset.CoreV1().RESTClient().Post(). - Resource("pods"). - Name(podName). - Namespace(_namespace).SubResource("exec") - req.VersionedParams( - &corev1.PodExecOptions{ - Command: cmd, - Stdin: false, - Stdout: true, - Stderr: true, - TTY: false, - }, - scheme.ParameterCodec, - ) - - var stdout, stderr bytes.Buffer - exec, err := remotecommand.NewSPDYExecutor(f.restConfig, "POST", req.URL()) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "request kubernetes exec api") - _ = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{ - Stdin: nil, - Stdout: &stdout, - Stderr: &stderr, - }) - return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()) -} - -func (f *Framework) LoginDashboardBySAML( - loginPath string, - username, password string, - redirectURI string, - acsErrmsg string, -) ([]*http.Cookie, []*http.Cookie) { - client := http.DefaultClient - client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - } - u, err := url.Parse(DashboardEndpoint) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - u.Path = loginPath - u.RawQuery = fmt.Sprintf("redirect_uri=%s", redirectURI) - - // 1: get location to keycloak - req, err := http.NewRequest(http.MethodGet, u.String(), nil) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - resp, err := client.Do(req) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "sending user login request") - defer func() { _ = resp.Body.Close() }() - - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(302)) - location := resp.Header.Get("Location") - f.GomegaT.Expect(location).Should(gomega.ContainSubstring("SAMLRequest")) - consoleCookie := resp.Header.Values("Set-Cookie") - - // 2: request keycloak, keycloak return an html page - req, err = http.NewRequest(http.MethodGet, location, nil) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - resp, err = client.Do(req) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "sending login page request") - defer func() { _ = resp.Body.Close() }() - keycloakCookie := resp.Header.Values("Set-Cookie") - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(200)) - doc, err := html.Parse(resp.Body) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "parse login page html") - - HTMLMap := f.ParseHTML(doc) - loginURL := HTMLMap["kc-form-login"] - - // 3: request keycloak login API, keycloak login success, and then return redirect HTML page - form := url.Values{} - form.Add("username", username) - form.Add("password", password) - req, err = http.NewRequest(http.MethodPost, loginURL, strings.NewReader(form.Encode())) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set("Cookie", strings.Join(keycloakCookie, ";")) - - resp, err = client.Do(req) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - defer func() { _ = resp.Body.Close() }() - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(200)) - - // save login keycloak cookies, for logout if we need - keycloakLoginCookies := resp.Cookies() - - // 4: callback to dashboard acs URL - doc, err = html.Parse(resp.Body) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - HTMLMap = f.ParseHTML(doc) - acsURL := HTMLMap["saml-post-binding"] - form = url.Values{} - form.Add("SAMLResponse", HTMLMap["SAMLResponse"]) - form.Add("RelayState", HTMLMap["RelayState"]) - req, err = http.NewRequest(http.MethodPost, acsURL, strings.NewReader(form.Encode())) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set("Cookie", strings.Join(consoleCookie, ";")) - - resp, err = client.Do(req) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - defer func() { _ = resp.Body.Close() }() - if len(acsErrmsg) > 0 { - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(http.StatusSeeOther)) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - f.GomegaT.Expect(resp.Header.Get("Location")).Should(gomega.ContainSubstring("/login?err_msg")) - f.GomegaT.Expect(resp.Header.Get("Location")).Should(gomega.ContainSubstring(url.QueryEscape(acsErrmsg))) - return nil, nil - } - - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(302)) - location = resp.Header.Get("Location") - u, err = url.Parse(location) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - f.GomegaT.Expect(u.EscapedPath()).Should(gomega.Equal(redirectURI)) - return resp.Cookies(), keycloakLoginCookies -} - -func (f *Framework) LogoutDashboardBySAML( - logoutPath string, - cookies []*http.Cookie, - keycloakCookies []*http.Cookie, - redirectURI string, - logoutIDPSession bool, - logoutErrMsg string, -) { - client := http.DefaultClient - client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - } - u, err := url.Parse(DashboardEndpoint) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - u.Path = logoutPath - u.RawQuery = fmt.Sprintf("redirect_uri=%s", redirectURI) - - // 1: request logout path, get location to keycloak - req, err := http.NewRequest(http.MethodGet, u.String(), nil) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - // set dashboard login cookies - for _, cookie := range cookies { - req.AddCookie(cookie) - } - resp, err := client.Do(req) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - defer func() { _ = resp.Body.Close() }() - if len(logoutErrMsg) > 0 { - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(http.StatusSeeOther)) - f.GomegaT.Expect(resp.Header.Get("Location")).Should(gomega.ContainSubstring("/login?err_msg")) - f.GomegaT.Expect(resp.Header.Get("Location")).Should(gomega.ContainSubstring(url.QueryEscape(logoutErrMsg))) - return - } - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(302)) - location := resp.Header.Get("Location") - if !logoutIDPSession { - f.GomegaT.Expect(location).Should(gomega.Equal(redirectURI)) - return - } - f.GomegaT.Expect(location).Should(gomega.ContainSubstring("SAMLRequest")) - - // 2: request keycloak, keycloak return an html page - req, err = http.NewRequest(http.MethodGet, location, nil) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - // set keycloak login cookies - for _, cookie := range keycloakCookies { - req.AddCookie(cookie) - } - resp, err = client.Do(req) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "sending login page request") - defer func() { _ = resp.Body.Close() }() - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(200)) - doc, err := html.Parse(resp.Body) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "parse login page html") - - // 3: request slo URL - HTMLMap := f.ParseHTML(doc) - sloURL := HTMLMap["saml-post-binding"] - form := url.Values{} - form.Add("SAMLResponse", HTMLMap["SAMLResponse"]) - form.Add("RelayState", HTMLMap["RelayState"]) - req, err = http.NewRequest(http.MethodPost, sloURL, strings.NewReader(form.Encode())) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - resp, err = client.Do(req) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - defer func() { _ = resp.Body.Close() }() - f.GomegaT.Expect(resp.StatusCode).Should(gomega.Equal(302)) - f.GomegaT.Expect(resp.Header.Get("Location")).Should(gomega.Equal(redirectURI)) -} - -func (f *Framework) LogoutDashboardByOIDC(logoutPath string, cookies []*http.Cookie) error { - client := http.DefaultClient - u, err := url.Parse(DashboardEndpoint) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred()) - u.Path = logoutPath - - req, err := http.NewRequest(http.MethodGet, u.String(), nil) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - for _, cookie := range cookies { - req.AddCookie(cookie) - } - - resp, err := client.Do(req) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "sending user logout request") - defer func() { - _ = resp.Body.Close() - }() - return nil -} - -func (f *Framework) GetPodLogs(name string, previous bool) string { - reader, err := f.clientset.CoreV1(). - Pods(_namespace). - GetLogs(name, &corev1.PodLogOptions{Previous: previous}). - Stream(context.Background()) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "get logs") - defer func() { - _ = reader.Close() - }() - - logs, err := io.ReadAll(reader) - f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred(), "read all logs") - - return string(logs) -} - -func (f *Framework) WaitMTLSDPLog(keyword string, sinceSeconds int64, timeout time.Duration) { - f.WaitPodsLog("app.kubernetes.io/name=apisix,cp-connection=mtls", keyword, sinceSeconds, timeout) -} - -func (f *Framework) WaitControllerManagerLog(keyword string, sinceSeconds int64, timeout time.Duration) { - f.WaitPodsLog("control-plane=controller-manager", keyword, sinceSeconds, timeout) -} - -func (f *Framework) WaitDPLog(keyword string, sinceSeconds int64, timeout time.Duration) { - f.WaitPodsLog("app.kubernetes.io/name=apisix", keyword, sinceSeconds, timeout) -} - -func (f *Framework) WaitPodsLog(selector, keyword string, sinceSeconds int64, timeout time.Duration) { - pods := f.ListRunningPods(selector) - wg := sync.WaitGroup{} - for _, p := range pods { - wg.Add(1) - go func(p corev1.Pod) { - defer wg.Done() - opts := corev1.PodLogOptions{Follow: true} - if sinceSeconds > 0 { - opts.SinceSeconds = ptr.To(sinceSeconds) - } else { - opts.TailLines = ptr.To(int64(0)) - } - logStream, err := f.clientset.CoreV1().Pods(p.Namespace).GetLogs(p.Name, &opts).Stream(context.Background()) - f.GomegaT.Expect(err).Should(gomega.BeNil()) - scanner := bufio.NewScanner(logStream) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, keyword) { - return - } - } - }(p) - } - c := make(chan struct{}) - go func() { - defer close(c) - wg.Wait() - }() - select { - case <-c: - return - case <-time.After(timeout): - f.GinkgoT.Error("wait log timeout") - } -} - -func CreateTestZipFile(sourceCode, metadata string) ([]byte, error) { - // Create a new zip file - zipBuffer := new(bytes.Buffer) - zipWriter := zip.NewWriter(zipBuffer) - - // Add files to the zip archive - if err := addFileToZip(zipWriter, "plugin.lua", sourceCode); err != nil { - return nil, err - } - if err := addFileToZip(zipWriter, "metadata.json", metadata); err != nil { - return nil, err - } - - // Close the zip writer - err := zipWriter.Close() - if err != nil { - return nil, err - } - - return zipBuffer.Bytes(), nil -} - -func HTTPRoutePolicyMustHaveCondition(t testing.TestingT, client client.Client, timeout time.Duration, refNN, hrpNN types.NamespacedName, - condition metav1.Condition) { - err := EventuallyHTTPRoutePolicyHaveStatus(client, timeout, hrpNN, func(httpRoutePolicy v1alpha1.HTTPRoutePolicy, status v1alpha1.PolicyStatus) bool { - for _, ancestor := range status.Ancestors { - if err := kubernetes.ConditionsHaveLatestObservedGeneration(&httpRoutePolicy, ancestor.Conditions); err != nil { - log.Printf("HTTPRoutePolicy %s (parentRef=%v) %v", hrpNN, parentRefToString(ancestor.AncestorRef), err) - return false - } - - if ancestor.AncestorRef.Name == gatewayv1.ObjectName(refNN.Name) && - (ancestor.AncestorRef.Namespace == nil || refNN.Namespace == "" || string(*ancestor.AncestorRef.Namespace) == refNN.Namespace) { - if findConditionInList(ancestor.Conditions, condition) { - log.Printf("found condition %v in list [%v] for %s reference %s", condition, ancestor.Conditions, hrpNN, refNN) - return true - } else { - log.Printf("not found condition %v in list [%v] for %s reference %s", condition, ancestor.Conditions, hrpNN, refNN) - } - } - } - return false - }) - - require.NoError(t, err, "error waiting for HTTPRoutePolicy status to have a Condition matching expectations") -} - -func EventuallyHTTPRoutePolicyHaveStatus(client client.Client, timeout time.Duration, hrpNN types.NamespacedName, - f func(httpRoutePolicy v1alpha1.HTTPRoutePolicy, status v1alpha1.PolicyStatus) bool) error { - _ = v1alpha1.AddToScheme(client.Scheme()) - return wait.PollUntilContextTimeout(context.Background(), time.Second, timeout, true, func(ctx context.Context) (done bool, err error) { - var httpRoutePolicy v1alpha1.HTTPRoutePolicy - if err = client.Get(ctx, hrpNN, &httpRoutePolicy); err != nil { - return false, errors.Errorf("error fetching HTTPRoutePolicy %v: %v", hrpNN, err) - } - return f(httpRoutePolicy, httpRoutePolicy.Status), nil - }) -} - -func addFileToZip(zipWriter *zip.Writer, fileName, fileContent string) error { - file, err := zipWriter.Create(fileName) - if err != nil { - return err - } - - _, err = file.Write([]byte(fileContent)) - return err -} - -func parentRefToString(p gatewayv1.ParentReference) string { - if p.Namespace != nil && *p.Namespace != "" { - return fmt.Sprintf("%v/%v", p.Namespace, p.Name) - } - return string(p.Name) -} - -func findConditionInList(conditions []metav1.Condition, expected metav1.Condition) bool { - return slices.ContainsFunc(conditions, func(item metav1.Condition) bool { - // an empty Status string means "Match any status". - // an empty Reason string means "Match any reason". - return expected.Type == item.Type && (expected.Status == "" || expected.Status == item.Status) && (expected.Reason == "" || expected.Reason == item.Reason) - }) -} diff --git a/test/e2e/gatewayapi/controller.go b/test/e2e/gatewayapi/controller.go deleted file mode 100644 index 5ded0be5a..000000000 --- a/test/e2e/gatewayapi/controller.go +++ /dev/null @@ -1,224 +0,0 @@ -// 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 gatewayapi - -import ( - "fmt" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/stretchr/testify/assert" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Check if controller cache gets synced with correct resources", func() { - - var gatewayProxyYaml = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - var defautlGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s - namespace: %s -spec: - controllerName: %s -` - - var defautlGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: %s - namespace: %s -spec: - gatewayClassName: %s - listeners: - - name: http1 - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var ResourceApplied = func(s *scaffold.Scaffold, resourType, resourceName, ns, resourceRaw string, observedGeneration int) { - Expect(s.CreateResourceFromStringWithNamespace(resourceRaw, ns)). - NotTo(HaveOccurred(), fmt.Sprintf("creating %s", resourType)) - - Eventually(func() string { - hryaml, err := s.GetResourceYamlFromNamespace(resourType, resourceName, ns) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("getting %s yaml", resourType)) - return hryaml - }, "8s", "2s"). - Should( - SatisfyAll( - ContainSubstring(`status: "True"`), - ContainSubstring(fmt.Sprintf("observedGeneration: %d", observedGeneration)), - ), - fmt.Sprintf("checking %s condition status", resourType), - ) - time.Sleep(1 * time.Second) - } - var beforeEach = func(s *scaffold.Scaffold, gatewayName string) { - err := s.CreateResourceFromString(fmt.Sprintf(` -kind: Namespace -apiVersion: v1 -metadata: - name: %s -`, gatewayName)) - Expect(err).NotTo(HaveOccurred(), "creating namespace") - By(fmt.Sprintf("create GatewayClass for controller %s", s.GetControllerName())) - - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err = s.CreateResourceFromStringWithNamespace(gatewayProxy, gatewayName) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - gatewayClassName := fmt.Sprintf("apisix-%d", time.Now().Unix()) - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defautlGatewayClass, gatewayClassName, gatewayName, s.GetControllerName()), gatewayName) - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(10 * time.Second) - - By("check GatewayClass condition") - gcyaml, err := s.GetResourceYamlFromNamespace("GatewayClass", gatewayClassName, gatewayName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defautlGateway, gatewayName, gatewayName, gatewayClassName), gatewayName) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(10 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYamlFromNamespace("Gateway", gatewayName, gatewayName) - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") - } - - Context("Create resource with first controller", func() { - s1 := scaffold.NewScaffold(&scaffold.Options{ - Name: "gateway1", - ControllerName: "apisix.apache.org/apisix-ingress-controller-1", - }) - var route1 = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin - namespace: gateway1 -spec: - parentRefs: - - name: gateway1 - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - filters: - - type: RequestMirror - requestMirror: - backendRef: - name: echo-service - port: 80 - backendRefs: - - name: httpbin-service-e2e-test - port: 80 - weight: 50 - - name: nginx - port: 80 - weight: 50 - ` - BeforeEach(func() { - beforeEach(s1, "gateway1") - }) - It("Apply resource ", func() { - ResourceApplied(s1, "HTTPRoute", "httpbin", "gateway1", route1, 1) - routes, err := s1.DefaultDataplaneResource().Route().List(s1.Context) - Expect(err).NotTo(HaveOccurred()) - Expect(routes).To(HaveLen(1)) - assert.Equal(GinkgoT(), routes[0].Labels["k8s/controller-name"], "apisix.apache.org/apisix-ingress-controller-1") - }) - }) - Context("Create resource with second controller", func() { - s2 := scaffold.NewScaffold(&scaffold.Options{ - Name: "gateway2", - ControllerName: "apisix.apache.org/apisix-ingress-controller-2", - }) - var route2 = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin2 - namespace: gateway2 -spec: - parentRefs: - - name: gateway2 - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - filters: - - type: RequestMirror - requestMirror: - backendRef: - name: echo-service - port: 80 - backendRefs: - - name: httpbin-service-e2e-test - port: 80 - weight: 50 - - name: nginx - port: 80 - weight: 50 -` - BeforeEach(func() { - beforeEach(s2, "gateway2") - }) - It("Apply resource ", func() { - ResourceApplied(s2, "HTTPRoute", "httpbin2", "gateway2", route2, 1) - routes, err := s2.DefaultDataplaneResource().Route().List(s2.Context) - Expect(err).NotTo(HaveOccurred()) - Expect(routes).To(HaveLen(1)) - assert.Equal(GinkgoT(), routes[0].Labels["k8s/controller-name"], "apisix.apache.org/apisix-ingress-controller-2") - }) - }) -}) diff --git a/test/e2e/gatewayapi/gateway.go b/test/e2e/gatewayapi/gateway.go deleted file mode 100644 index 39137eed9..000000000 --- a/test/e2e/gatewayapi/gateway.go +++ /dev/null @@ -1,298 +0,0 @@ -// 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 gatewayapi - -import ( - "context" - "fmt" - "strings" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/stretchr/testify/assert" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -const _secretName = "test-apisix-tls" - -var Cert = strings.TrimSpace(framework.TestServerCert) - -var Key = strings.TrimSpace(framework.TestServerKey) - -func createSecret(s *scaffold.Scaffold, secretName string) { - err := s.NewKubeTlsSecret(secretName, Cert, Key) - assert.Nil(GinkgoT(), err, "create secret error") -} - -var _ = Describe("Test Gateway", func() { - s := scaffold.NewScaffold(&scaffold.Options{ - ControllerName: "apisix.apache.org/apisix-ingress-controller", - }) - - var gatewayProxyYaml = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - Context("Gateway", func() { - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: apisix -spec: - controllerName: "apisix.apache.org/apisix-ingress-controller" -` - - var defaultGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: apisix - listeners: - - name: http1 - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var noClassGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix-not-class -spec: - gatewayClassName: apisix-not-exist - listeners: - - name: http1 - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - It("Create Gateway", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create GatewayClass") - err = s.CreateResourceFromStringWithNamespace(defaultGatewayClass, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("check GatewayClass condition") - gcyaml, err := s.GetResourceYaml("GatewayClass", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(defaultGateway, s. - Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") - - By("create Gateway with not accepted GatewayClass") - err = s.CreateResourceFromStringWithNamespace(noClassGateway, s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err = s.GetResourceYaml("Gateway", "apisix-not-class") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: Unknown`), "checking Gateway condition status") - }) - }) - - Context("Gateway SSL", func() { - It("Check if SSL resource was created", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create secret") - secretName := _secretName - host := "api6.com" - createSecret(s, secretName) - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: apisix -spec: - controllerName: "apisix.apache.org/apisix-ingress-controller" -` - - var defaultGateway = fmt.Sprintf(` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: apisix - listeners: - - name: http1 - protocol: HTTPS - port: 443 - hostname: %s - tls: - certificateRefs: - - kind: Secret - group: "" - name: %s - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -`, host, secretName) - By("create GatewayClass") - err = s.CreateResourceFromStringWithNamespace(defaultGatewayClass, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(defaultGateway, s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(10 * time.Second) - - tls, err := s.DefaultDataplaneResource().SSL().List(context.Background()) - assert.Nil(GinkgoT(), err, "list tls error") - assert.Len(GinkgoT(), tls, 1, "tls number not expect") - assert.Equal(GinkgoT(), Cert, tls[0].Cert, "tls cert not expect") - assert.ElementsMatch(GinkgoT(), []string{host, "*.api6.com"}, tls[0].Snis) - }) - - Context("Gateway SSL with and without hostname", func() { - It("Check if SSL resource was created and updated", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - secretName := _secretName - createSecret(s, secretName) - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: apisix -spec: - controllerName: "apisix.apache.org/apisix-ingress-controller" -` - - var defaultGateway = fmt.Sprintf(` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: same-namespace-with-https-listener -spec: - gatewayClassName: apisix - listeners: - - name: https - port: 443 - protocol: HTTPS - allowedRoutes: - namespaces: - from: Same - tls: - certificateRefs: - - group: "" - kind: Secret - name: %s - - name: https-with-hostname - port: 443 - hostname: api6.com - protocol: HTTPS - allowedRoutes: - namespaces: - from: Same - tls: - certificateRefs: - - group: "" - kind: Secret - name: %s - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -`, secretName, secretName) - By("create GatewayClass") - err = s.CreateResourceFromStringWithNamespace(defaultGatewayClass, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(defaultGateway, s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(10 * time.Second) - - tls, err := s.DefaultDataplaneResource().SSL().List(context.Background()) - assert.Nil(GinkgoT(), err, "list tls error") - assert.Len(GinkgoT(), tls, 1, "tls number not expect") - assert.Equal(GinkgoT(), Cert, tls[0].Cert, "tls cert not expect") - assert.Equal(GinkgoT(), tls[0].Labels["k8s/controller-name"], "apisix.apache.org/apisix-ingress-controller") - - By("update secret") - err = s.NewKubeTlsSecret(secretName, framework.TestCert, framework.TestKey) - Expect(err).NotTo(HaveOccurred(), "update secret") - Eventually(func() string { - tls, err := s.DefaultDataplaneResource().SSL().List(context.Background()) - Expect(err).NotTo(HaveOccurred(), "list ssl from dashboard") - if len(tls) < 1 { - return "" - } - return tls[0].Cert - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(framework.TestCert)) - }) - }) - }) - -}) diff --git a/test/e2e/gatewayapi/gatewayclass.go b/test/e2e/gatewayapi/gatewayclass.go deleted file mode 100644 index 803fb0fa8..000000000 --- a/test/e2e/gatewayapi/gatewayclass.go +++ /dev/null @@ -1,126 +0,0 @@ -// 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 gatewayapi - -import ( - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Test GatewayClass", func() { - s := scaffold.NewScaffold(&scaffold.Options{ - ControllerName: "apisix.apache.org/apisix-ingress-controller", - }) - - Context("Create GatewayClass", func() { - var defautlGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: apisix -spec: - controllerName: "apisix.apache.org/apisix-ingress-controller" -` - - var noGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: apisix-not-accepeted -spec: - controllerName: "apisix.apache.org/not-exist" -` - const defaultGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: apisix - listeners: - - name: http1 - protocol: HTTP - port: 80 -` - It("Create GatewayClass", func() { - By("create default GatewayClass") - err := s.CreateResourceFromStringWithNamespace(defautlGatewayClass, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - gcyaml, err := s.GetResourceYaml("GatewayClass", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("create GatewayClass with not accepted") - err = s.CreateResourceFromStringWithNamespace(noGatewayClass, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - gcyaml, err = s.GetResourceYaml("GatewayClass", "apisix-not-accepeted") - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: Unknown`), "checking GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: Waiting for controller"), "checking GatewayClass condition message") - }) - - It("Delete GatewayClass", func() { - By("create default GatewayClass") - err := s.CreateResourceFromStringWithNamespace(defautlGatewayClass, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - Eventually(func() string { - spec, err := s.GetResourceYaml("GatewayClass", "apisix") - Expect(err).NotTo(HaveOccurred(), "get resource yaml") - return spec - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring(`status: "True"`)) - - By("create a Gateway") - err = s.CreateResourceFromStringWithNamespace(defaultGateway, s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(time.Second) - - By("try to delete the GatewayClass") - _, err = s.RunKubectlAndGetOutput("delete", "GatewayClass", "apisix", "--wait=false") - Expect(err).NotTo(HaveOccurred()) - - _, err = s.GetResourceYaml("GatewayClass", "apisix") - Expect(err).NotTo(HaveOccurred(), "get resource yaml") - - output, err := s.RunKubectlAndGetOutput("describe", "GatewayClass", "apisix") - Expect(err).NotTo(HaveOccurred(), "describe GatewayClass apisix") - Expect(output).To(And( - ContainSubstring("Warning"), - ContainSubstring("DeletionBlocked"), - ContainSubstring("gatewayclass-controller"), - ContainSubstring("the GatewayClass is still used by Gateways"), - )) - - By("delete the Gateway") - err = s.DeleteResource("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "deleting Gateway") - time.Sleep(time.Second) - - By("try to delete the GatewayClass again") - err = s.DeleteResource("GatewayClass", "apisix") - Expect(err).NotTo(HaveOccurred()) - - _, err = s.GetResourceYaml("GatewayClass", "apisix") - Expect(err).To(HaveOccurred(), "get resource yaml") - Expect(err.Error()).To(ContainSubstring("not found")) - }) - }) -}) diff --git a/test/e2e/gatewayapi/gatewayproxy.go b/test/e2e/gatewayapi/gatewayproxy.go deleted file mode 100644 index 2cbdcf977..000000000 --- a/test/e2e/gatewayapi/gatewayproxy.go +++ /dev/null @@ -1,395 +0,0 @@ -// 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 gatewayapi - -import ( - "fmt" - "net/http" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Test GatewayProxy", func() { - s := scaffold.NewDefaultScaffold() - - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s -spec: - controllerName: %s -` - - var gatewayWithProxy = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: %s - listeners: - - name: http - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var gatewayProxyWithEnabledPlugin = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: response-rewrite - enabled: true - config: - headers: - X-Proxy-Test: "enabled" -` - - var gatewayProxyWithDisabledPlugin = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: response-rewrite - enabled: false - config: - headers: - X-Proxy-Test: "disabled" -` - var ( - gatewayProxyWithPluginMetadata0 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: error-page - enabled: true - config: {} - pluginMetadata: - error-page: { - "enable": true, - "error_404": { - "body": "404 from plugin metadata", - "content-type": "text/plain" - } - } -` - gatewayProxyWithPluginMetadata1 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: error-page - enabled: true - config: {} - pluginMetadata: - error-page: { - "enable": false, - "error_404": { - "body": "404 from plugin metadata", - "content-type": "text/plain" - } - } -` - ) - - var httpRouteForTest = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: test-route -spec: - parentRefs: - - name: %s - hostnames: - - example.com - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var resourceApplied = func(resourceType, resourceName, resourceRaw string, observedGeneration int) { - Expect(s.CreateResourceFromString(resourceRaw)). - NotTo(HaveOccurred(), fmt.Sprintf("creating %s", resourceType)) - - Eventually(func() string { - hryaml, err := s.GetResourceYaml(resourceType, resourceName) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("getting %s yaml", resourceType)) - return hryaml - }).WithTimeout(8*time.Second).ProbeEvery(2*time.Second). - Should( - SatisfyAll( - ContainSubstring(`status: "True"`), - ContainSubstring(fmt.Sprintf("observedGeneration: %d", observedGeneration)), - ), - fmt.Sprintf("checking %s condition status", resourceType), - ) - } - - var ( - gatewayClassName string - ) - - BeforeEach(func() { - By("Create GatewayClass") - gatewayClassName = fmt.Sprintf("apisix-%d", time.Now().Unix()) - err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()), "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("Check GatewayClass condition") - gcYaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcYaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcYaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("Create GatewayProxy with enabled plugin") - err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithEnabledPlugin, framework.DashboardTLSEndpoint, s.AdminKey())) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy with enabled plugin") - time.Sleep(5 * time.Second) - - By("Create Gateway with GatewayProxy") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayWithProxy, gatewayClassName), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway with GatewayProxy") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") - }) - - AfterEach(func() { - By("Clean up resources") - _ = s.DeleteResourceFromString(fmt.Sprintf(httpRouteForTest, "apisix")) - _ = s.DeleteResourceFromString(fmt.Sprintf(gatewayWithProxy, gatewayClassName)) - _ = s.DeleteResourceFromString(fmt.Sprintf(gatewayProxyWithEnabledPlugin, framework.DashboardTLSEndpoint, s.AdminKey())) - }) - - Context("Test Gateway with enabled GatewayProxy plugin", func() { - It("Should apply plugin configuration when enabled", func() { - By("Create HTTPRoute for Gateway with GatewayProxy") - resourceApplied("HTTPRoute", "test-route", fmt.Sprintf(httpRouteForTest, "apisix"), 1) - - By("Check if the plugin is applied") - resp := s.NewAPISIXClient(). - GET("/get"). - WithHost("example.com"). - Expect(). - Status(200) - - resp.Header("X-Proxy-Test").IsEqual("enabled") - - By("Update GatewayProxy with disabled plugin") - err := s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithDisabledPlugin, framework.DashboardTLSEndpoint, s.AdminKey())) - Expect(err).NotTo(HaveOccurred(), "updating GatewayProxy with disabled plugin") - time.Sleep(5 * time.Second) - - By("Create HTTPRoute for Gateway with GatewayProxy") - resourceApplied("HTTPRoute", "test-route", fmt.Sprintf(httpRouteForTest, "apisix"), 1) - - By("Check if the plugin is not applied") - resp = s.NewAPISIXClient(). - GET("/get"). - WithHost("example.com"). - Expect(). - Status(200) - - resp.Header("X-Proxy-Test").IsEmpty() - }) - }) - - Context("Test Gateway with PluginMetadata", func() { - var ( - err error - ) - - It("Should work OK with error-page", func() { - By("Update GatewayProxy with PluginMetadata") - err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata0, framework.DashboardTLSEndpoint, s.AdminKey())) - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(5 * time.Second) - - By("Create HTTPRoute for Gateway with GatewayProxy") - resourceApplied("HTTPRoute", "test-route", fmt.Sprintf(httpRouteForTest, "apisix"), 1) - - By("Check PluginMetadata working") - s.NewAPISIXClient(). - GET("/not-found"). - WithHost("example.com"). - Expect(). - Status(http.StatusNotFound). - Body().Contains("404 from plugin metadata") - - By("Update GatewayProxy with PluginMetadata") - err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata1, framework.DashboardTLSEndpoint, s.AdminKey())) - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(5 * time.Second) - - By("Check PluginMetadata working") - s.NewAPISIXClient(). - GET("/not-found"). - WithHost("example.com"). - Expect(). - Status(http.StatusNotFound). - Body().Contains(`{"error_msg":"404 Route Not Found"}`) - - By("Delete GatewayProxy") - err = s.DeleteResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata0, framework.DashboardTLSEndpoint, s.AdminKey())) - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(5 * time.Second) - - By("Check PluginMetadata is not working") - s.NewAPISIXClient(). - GET("/not-found"). - WithHost("example.com"). - Expect(). - Status(http.StatusNotFound). - Body().Contains(`{"error_msg":"404 Route Not Found"}`) - }) - }) - - var ( - gatewayProxyWithInvalidProviderType = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: "InvalidType" -` - gatewayProxyWithMissingControlPlane = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: "ControlPlane" -` - gatewayProxyWithValidProvider = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: "ControlPlane" - controlPlane: - endpoints: - - "http://localhost:9180" - auth: - type: "AdminKey" - adminKey: - value: "test-key" -` - ) - - Context("Test GatewayProxy Provider Validation", func() { - AfterEach(func() { - By("Clean up GatewayProxy resources") - _ = s.DeleteResourceFromString(gatewayProxyWithInvalidProviderType) - _ = s.DeleteResourceFromString(gatewayProxyWithMissingControlPlane) - _ = s.DeleteResourceFromString(gatewayProxyWithValidProvider) - }) - - It("Should reject invalid provider type", func() { - By("Create GatewayProxy with invalid provider type") - err := s.CreateResourceFromString(gatewayProxyWithInvalidProviderType) - Expect(err).To(HaveOccurred(), "creating GatewayProxy with invalid provider type") - Expect(err.Error()).To(ContainSubstring("Invalid value")) - }) - - It("Should reject missing controlPlane configuration", func() { - By("Create GatewayProxy with missing controlPlane") - err := s.CreateResourceFromString(gatewayProxyWithMissingControlPlane) - Expect(err).To(HaveOccurred(), "creating GatewayProxy with missing controlPlane") - Expect(err.Error()).To(ContainSubstring("controlPlane must be specified when type is ControlPlane")) - }) - - It("Should accept valid provider configuration", func() { - By("Create GatewayProxy with valid provider") - err := s.CreateResourceFromString(gatewayProxyWithValidProvider) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy with valid provider") - - Eventually(func() string { - gpYaml, err := s.GetResourceYaml("GatewayProxy", "apisix-proxy-config") - Expect(err).NotTo(HaveOccurred(), "getting GatewayProxy yaml") - return gpYaml - }).WithTimeout(8*time.Second).ProbeEvery(2*time.Second). - Should(ContainSubstring(`"type":"ControlPlane"`), "checking GatewayProxy is applied") - }) - }) -}) diff --git a/test/e2e/gatewayapi/httproute.go b/test/e2e/gatewayapi/httproute.go deleted file mode 100644 index d710875a3..000000000 --- a/test/e2e/gatewayapi/httproute.go +++ /dev/null @@ -1,1714 +0,0 @@ -// 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 gatewayapi - -import ( - "context" - "fmt" - "net/http" - "strings" - "time" - - "github.com/gruntwork-io/terratest/modules/retry" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/pkg/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Test HTTPRoute", func() { - s := scaffold.NewDefaultScaffold() - - var gatewayProxyYaml = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - var gatewayClassYaml = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s -spec: - controllerName: %s -` - - var defaultGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: %s - listeners: - - name: http1 - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - var defaultGatewayHTTPS = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: %s - listeners: - - name: http1 - protocol: HTTPS - port: 443 - hostname: api6.com - tls: - certificateRefs: - - kind: Secret - group: "" - name: test-apisix-tls - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var ResourceApplied = func(resourType, resourceName, resourceRaw string, observedGeneration int) { - Expect(s.CreateResourceFromString(resourceRaw)). - NotTo(HaveOccurred(), fmt.Sprintf("creating %s", resourType)) - - Eventually(func() string { - hryaml, err := s.GetResourceYaml(resourType, resourceName) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("getting %s yaml", resourType)) - return hryaml - }, "8s", "2s"). - Should( - SatisfyAll( - ContainSubstring(`status: "True"`), - ContainSubstring(fmt.Sprintf("observedGeneration: %d", observedGeneration)), - ), - fmt.Sprintf("checking %s condition status", resourType), - ) - time.Sleep(1 * time.Second) - } - - var beforeEachHTTP = func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create GatewayClass") - gatewayClassName := fmt.Sprintf("apisix-%d", time.Now().Unix()) - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayClassYaml, gatewayClassName, s.GetControllerName()), "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("check GatewayClass condition") - gcyaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGateway, gatewayClassName), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") - } - - var beforeEachHTTPS = func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - secretName := _secretName - createSecret(s, secretName) - By("create GatewayClass") - gatewayClassName := fmt.Sprintf("apisix-%d", time.Now().Unix()) - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayClassYaml, gatewayClassName, s.GetControllerName()), "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("check GatewayClass condition") - gcyaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGatewayHTTPS, gatewayClassName), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") - } - Context("HTTPRoute with HTTPS Gateway", func() { - var exactRouteByGet = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - api6.com - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - BeforeEach(beforeEachHTTPS) - - It("Create/Updtea/Delete HTTPRoute", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", exactRouteByGet, 1) - - By("access dataplane to check the HTTPRoute") - s.NewAPISIXHttpsClient("api6.com"). - GET("/get"). - WithHost("api6.com"). - Expect(). - Status(200) - By("delete HTTPRoute") - err := s.DeleteResourceFromString(exactRouteByGet) - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoute") - time.Sleep(5 * time.Second) - - s.NewAPISIXHttpsClient("api6.com"). - GET("/get"). - WithHost("api6.com"). - Expect(). - Status(404) - }) - }) - - Context("HTTPRoute with Multiple Gateway", func() { - var additionalGatewayGroupID string - var additionalNamespace string - var additionalGatewayClassName string - - var additionalGatewayProxyYaml = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: additional-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - var additionalGateway = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: additional-gateway -spec: - gatewayClassName: %s - listeners: - - name: http-additional - protocol: HTTP - port: 80 - allowedRoutes: - namespaces: - from: All - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: additional-proxy-config -` - - // HTTPRoute that references both gateways - var multiGatewayHTTPRoute = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: multi-gateway-route -spec: - parentRefs: - - name: apisix - namespace: %s - - name: additional-gateway - namespace: %s - hostnames: - - httpbin.example - - httpbin-additional.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - BeforeEach(func() { - beforeEachHTTP() - - By("Create additional gateway group") - var err error - additionalGatewayGroupID, additionalNamespace, err = s.CreateAdditionalGatewayGroup("multi-gw") - Expect(err).NotTo(HaveOccurred(), "creating additional gateway group") - - By("Create additional GatewayProxy") - // Get admin key for the additional gateway group - resources, exists := s.GetAdditionalGatewayGroup(additionalGatewayGroupID) - Expect(exists).To(BeTrue(), "additional gateway group should exist") - - By("Create additional GatewayClass") - additionalGatewayClassName = fmt.Sprintf("apisix-%d", time.Now().Unix()) - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayClassYaml, additionalGatewayClassName, s.GetControllerName()), "") - Expect(err).NotTo(HaveOccurred(), "creating additional GatewayClass") - time.Sleep(5 * time.Second) - By("Check additional GatewayClass condition") - gcyaml, err := s.GetResourceYaml("GatewayClass", additionalGatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting additional GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking additional GatewayClass condition status") - Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking additional GatewayClass condition message") - - additionalGatewayProxy := fmt.Sprintf(additionalGatewayProxyYaml, framework.DashboardTLSEndpoint, resources.AdminAPIKey) - err = s.CreateResourceFromStringWithNamespace(additionalGatewayProxy, additionalNamespace) - Expect(err).NotTo(HaveOccurred(), "creating additional GatewayProxy") - - By("Create additional Gateway") - err = s.CreateResourceFromStringWithNamespace( - fmt.Sprintf(additionalGateway, additionalGatewayClassName), - additionalNamespace, - ) - Expect(err).NotTo(HaveOccurred(), "creating additional Gateway") - time.Sleep(5 * time.Second) - }) - - It("HTTPRoute should be accessible through both gateways", func() { - By("Create HTTPRoute referencing both gateways") - multiGatewayRoute := fmt.Sprintf(multiGatewayHTTPRoute, s.Namespace(), additionalNamespace) - ResourceApplied("HTTPRoute", "multi-gateway-route", multiGatewayRoute, 1) - - By("Access through default gateway") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(http.StatusOK) - - By("Access through additional gateway") - client, err := s.NewAPISIXClientForGatewayGroup(additionalGatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "creating client for additional gateway") - - client. - GET("/get"). - WithHost("httpbin-additional.example"). - Expect(). - Status(http.StatusOK) - - By("Delete Additional Gateway") - err = s.DeleteResourceFromStringWithNamespace(fmt.Sprintf(additionalGateway, additionalGatewayClassName), additionalNamespace) - Expect(err).NotTo(HaveOccurred(), "deleting additional Gateway") - time.Sleep(5 * time.Second) - - By("HTTPRoute should still be accessible through default gateway") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(http.StatusOK) - - By("HTTPRoute should not be accessible through additional gateway") - client, err = s.NewAPISIXClientForGatewayGroup(additionalGatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "creating client for additional gateway") - - client. - GET("/get"). - WithHost("httpbin-additional.example"). - Expect(). - Status(http.StatusNotFound) - }) - }) - - Context("HTTPRoute Base", func() { - var httprouteWithExternalName = ` -apiVersion: v1 -kind: Service -metadata: - name: httpbin-external-domain -spec: - type: ExternalName - externalName: postman-echo.com ---- -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.external - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-external-domain - port: 80 -` - var exactRouteByGet = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - var exactRouteByGet2 = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin2 -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin2.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - var invalidBackendPort = ` -apiVersion: v1 -kind: Service -metadata: - name: httpbin-multiple-port -spec: - selector: - app: httpbin-deployment-e2e-test - ports: - - name: http - port: 80 - protocol: TCP - targetPort: 80 - - name: invalid - port: 10031 - protocol: TCP - targetPort: 10031 - - name: http2 - port: 8080 - protocol: TCP - targetPort: 80 - type: ClusterIP ---- -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-multiple-port - port: 80 -` - - BeforeEach(beforeEachHTTP) - - It("Create/Update/Delete HTTPRoute", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", exactRouteByGet, 1) - - By("access dataplane to check the HTTPRoute") - s.NewAPISIXClient(). - GET("/get"). - Expect(). - Status(404) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - By("delete HTTPRoute") - err := s.DeleteResourceFromString(exactRouteByGet) - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoute") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(404) - }) - - It("Delete Gateway after apply HTTPRoute", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", exactRouteByGet, 1) - - By("access daataplane to check the HTTPRoute") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - By("delete Gateway") - err := s.DeleteResource("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "deleting Gateway") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(404) - }) - - It("Proxy External Service", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", httprouteWithExternalName, 1) - - By("checking the external service response") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.external"). - Expect(). - Status(200) - }) - - It("Match Port", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", invalidBackendPort, 1) - - serviceResources, err := s.DefaultDataplaneResource().Service().List(context.Background()) - Expect(err).NotTo(HaveOccurred(), "listing services") - Expect(serviceResources).To(HaveLen(1), "checking service length") - - serviceResource := serviceResources[0] - nodes := serviceResource.Upstream.Nodes - Expect(nodes).To(HaveLen(1), "checking nodes length") - Expect(nodes[0].Port).To(Equal(80)) - }) - - It("Delete HTTPRoute during restart", func() { - By("create HTTPRoute httpbin") - ResourceApplied("HTTPRoute", "httpbin", exactRouteByGet, 1) - - By("create HTTPRoute httpbin2") - ResourceApplied("HTTPRoute", "httpbin2", exactRouteByGet2, 1) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin2.example"). - Expect(). - Status(200) - - s.ScaleIngress(0) - - By("delete HTTPRoute httpbin2") - err := s.DeleteResource("HTTPRoute", "httpbin2") - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoute httpbin2") - - s.ScaleIngress(1) - time.Sleep(1 * time.Minute) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin2.example"). - Expect(). - Status(404) - }) - }) - - Context("HTTPRoute Rule Match", func() { - var exactRouteByGet = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - var varsRoute = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - headers: - - type: Exact - name: X-Route-Name - value: httpbin - # name: get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - const httpRoutePolicy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-0 -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin - # sectionName: get - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin-1 - sectionName: get - priority: 10 - vars: - - - http_x_hrp_name - - == - - http-route-policy-0 - - - arg_hrp_name - - == - - http-route-policy-0 -` - - var prefixRouteByStatus = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: PathPrefix - value: /status - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var methodRouteGETAndDELETEByAnything = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /anything - method: GET - - path: - type: Exact - value: /anything - method: DELETE - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - BeforeEach(beforeEachHTTP) - - It("HTTPRoute Exact Match", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", exactRouteByGet, 1) - - By("access daataplane to check the HTTPRoute") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get/xxx"). - WithHost("httpbin.example"). - Expect(). - Status(404) - }) - - It("HTTPRoute Prefix Match", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", prefixRouteByStatus, 1) - - By("access daataplane to check the HTTPRoute") - s.NewAPISIXClient(). - GET("/status/200"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/status/201"). - WithHost("httpbin.example"). - Expect(). - Status(201) - }) - - It("HTTPRoute Method Match", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", methodRouteGETAndDELETEByAnything, 1) - - By("access daataplane to check the HTTPRoute") - s.NewAPISIXClient(). - GET("/anything"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - DELETE("/anything"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - POST("/anything"). - WithHost("httpbin.example"). - Expect(). - Status(404) - }) - - It("HTTPRoute Vars Match", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", varsRoute, 1) - - By("access dataplane to check the HTTPRoute") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(http.StatusNotFound) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect(). - Status(http.StatusOK) - }) - - It("HTTPRoutePolicy in effect", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", varsRoute, 1) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect(). - Status(http.StatusOK) - - By("create HTTPRoutePolicy") - ResourceApplied("HTTPRoutePolicy", "http-route-policy-0", httpRoutePolicy, 1) - - By("access dataplane to check the HTTPRoutePolicy") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect(). - Status(http.StatusNotFound) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - WithHeader("X-HRP-Name", "http-route-policy-0"). - WithQuery("hrp_name", "http-route-policy-0"). - Expect(). - Status(http.StatusOK) - - By("update HTTPRoutePolicy") - const changedHTTPRoutePolicy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-0 -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin - # sectionName: get - priority: 10 - vars: - - - http_x_hrp_name - - == - - new-hrp-name -` - ResourceApplied("HTTPRoutePolicy", "http-route-policy-0", changedHTTPRoutePolicy, 1) - // use the old vars cannot match any route - Eventually(func() int { - return s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - WithHeader("X-HRP-Name", "http-route-policy-0"). - WithQuery("hrp_name", "http-route-policy-0"). - Expect().Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) - - // use the new vars can match the route - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - WithHeader("X-HRP-Name", "new-hrp-name"). - Expect(). - Status(http.StatusOK) - - By("delete the HTTPRoutePolicy") - err := s.DeleteResource("HTTPRoutePolicy", "http-route-policy-0") - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoutePolicy") - Eventually(func() string { - _, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-0") - return err.Error() - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring(`httproutepolicies.apisix.apache.org "http-route-policy-0" not found`)) - // access the route without additional vars should be OK - message := retry.DoWithRetry(s.GinkgoT, "", 10, time.Second, func() (string, error) { - statusCode := s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect().Raw().StatusCode - if statusCode != http.StatusOK { - return "", errors.Errorf("unexpected status code: %v", statusCode) - } - return "request OK", nil - }) - s.Logf(message) - }) - - It("HTTPRoutePolicy conflicts", func() { - const httpRoutePolicy0 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-0 -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin - priority: 10 - vars: - - - http_x_hrp_name - - == - - http-route-policy-0 -` - const httpRoutePolicy1 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-1 -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin - priority: 10 - vars: - - - http_x_hrp_name - - == - - http-route-policy-0 -` - const httpRoutePolicy1Priority20 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-1 -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin - priority: 20 - vars: - - - http_x_hrp_name - - == - - http-route-policy-0 -` - const httpRoutePolicy2 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-2 -spec: - targetRefs: - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin - - group: gateway.networking.k8s.io - kind: HTTPRoute - name: httpbin-1 - priority: 30 - vars: - - - http_x_hrp_name - - == - - http-route-policy-0 -` - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", varsRoute, 1) - - By("create HTTPRoutePolices") - for name, spec := range map[string]string{ - "http-route-policy-0": httpRoutePolicy0, - "http-route-policy-1": httpRoutePolicy1, - "http-route-policy-2": httpRoutePolicy2, - } { - err := s.CreateResourceFromString(spec) - Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy") - // wait for HTTPRoutePolicy is Accepted - framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 10*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"}, - types.NamespacedName{Namespace: s.Namespace(), Name: name}, - metav1.Condition{ - Type: string(v1alpha2.PolicyConditionAccepted), - }, - ) - } - for _, name := range []string{"http-route-policy-0", "http-route-policy-1", "http-route-policy-2"} { - framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 10*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"}, - types.NamespacedName{Namespace: s.Namespace(), Name: name}, - metav1.Condition{ - Type: string(v1alpha2.PolicyConditionAccepted), - Status: metav1.ConditionFalse, - Reason: string(v1alpha2.PolicyReasonConflicted), - }, - ) - } - - // assert that conflict policies are not in effect - Eventually(func() int { - return s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect().Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) - - By("delete HTTPRoutePolicies") - err := s.DeleteResource("HTTPRoutePolicy", "http-route-policy-2") - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoutePolicy %s", "http-route-policy-2") - for _, name := range []string{"http-route-policy-0", "http-route-policy-1"} { - framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 10*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"}, - types.NamespacedName{Namespace: s.Namespace(), Name: name}, - metav1.Condition{ - Type: string(v1alpha2.PolicyConditionAccepted), - Status: metav1.ConditionTrue, - Reason: string(v1alpha2.PolicyReasonAccepted), - }, - ) - } - Eventually(func() int { - return s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect().Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) - - By("update HTTPRoutePolicy") - err = s.CreateResourceFromString(httpRoutePolicy1Priority20) - Expect(err).NotTo(HaveOccurred(), "update HTTPRoutePolicy's priority to 20") - framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 10*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"}, - types.NamespacedName{Namespace: s.Namespace(), Name: "http-route-policy-1"}, - metav1.Condition{ - Type: string(v1alpha2.PolicyConditionAccepted), - }, - ) - for _, name := range []string{"http-route-policy-0", "http-route-policy-1"} { - framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 10*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"}, - types.NamespacedName{Namespace: s.Namespace(), Name: name}, - metav1.Condition{ - Type: string(v1alpha2.PolicyConditionAccepted), - Status: metav1.ConditionFalse, - Reason: string(v1alpha2.PolicyReasonConflicted), - }, - ) - } - Eventually(func() int { - return s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect().Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) - }) - - It("HTTPRoutePolicy status changes on HTTPRoute deleting", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", varsRoute, 1) - - By("create HTTPRoutePolicy") - ResourceApplied("HTTPRoutePolicy", "http-route-policy-0", httpRoutePolicy, 1) - - Eventually(func() string { - spec, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-0") - Expect(err).NotTo(HaveOccurred(), "getting HTTPRoutePolicy") - return spec - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring("type: Accepted")) - - By("access dataplane to check the HTTPRoutePolicy") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - Expect(). - Status(http.StatusNotFound) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - WithHeader("X-HRP-Name", "http-route-policy-0"). - WithQuery("hrp_name", "http-route-policy-0"). - Expect(). - Status(http.StatusOK) - - By("delete the HTTPRoute, assert the HTTPRoutePolicy's status will be changed") - err := s.DeleteResource("HTTPRoute", "httpbin") - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoute") - message := retry.DoWithRetry(s.GinkgoT, "request the deleted route", 10, time.Second, func() (string, error) { - statusCode := s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - WithHeader("X-Route-Name", "httpbin"). - WithHeader("X-HRP-Name", "http-route-policy-0"). - WithQuery("hrp_name", "http-route-policy-0"). - Expect().Raw().StatusCode - if statusCode != http.StatusNotFound { - return "", errors.Errorf("unexpected status code: %v", statusCode) - } - return "the route is deleted", nil - }) - s.Logf(message) - - Eventually(func() string { - spec, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-0") - Expect(err).NotTo(HaveOccurred(), "getting HTTPRoutePolicy") - return spec - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).ShouldNot(ContainSubstring("ancestorRef:")) - }) - }) - - Context("HTTPRoute Filters", func() { - var reqHeaderModifyByHeaders = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /headers - filters: - - type: RequestHeaderModifier - requestHeaderModifier: - add: - - name: X-Req-Add - value: "add" - set: - - name: X-Req-Set - value: "set" - remove: - - X-Req-Removed - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var respHeaderModifyByHeaders = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /headers - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - add: - - name: X-Resp-Add - value: "add" - set: - - name: X-Resp-Set - value: "set" - remove: - - Server - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var httpsRedirectByHeaders = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /headers - filters: - - type: RequestRedirect - requestRedirect: - scheme: https - port: 9443 -` - - var hostnameRedirectByHeaders = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /headers - filters: - - type: RequestRedirect - requestRedirect: - hostname: httpbin.org - statusCode: 301 -` - - var replacePrefixMatch = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: PathPrefix - value: /replace - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: /status - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var replaceFullPathAndHost = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: PathPrefix - value: /replace - filters: - - type: URLRewrite - urlRewrite: - hostname: replace.example.org - path: - type: ReplaceFullPath - replaceFullPath: /headers - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var echoPlugin = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: PluginConfig -metadata: - name: example-plugin-config -spec: - plugins: - - name: echo - config: - body: "Hello, World!!" -` - var echoPluginUpdated = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: PluginConfig -metadata: - name: example-plugin-config -spec: - plugins: - - name: echo - config: - body: "Updated" -` - var extensionRefEchoPlugin = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - filters: - - type: ExtensionRef - extensionRef: - group: apisix.apache.org - kind: PluginConfig - name: example-plugin-config - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - BeforeEach(beforeEachHTTP) - - It("HTTPRoute RequestHeaderModifier", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", reqHeaderModifyByHeaders, 1) - - By("access daataplane to check the HTTPRoute") - respExp := s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.example"). - WithHeader("X-Req-Add", "test"). - WithHeader("X-Req-Removed", "test"). - WithHeader("X-Req-Set", "test"). - Expect() - - respExp.Status(200) - respExp.Body(). - Contains(`"X-Req-Add": "test,add"`). - Contains(`"X-Req-Set": "set"`). - NotContains(`"X-Req-Removed": "remove"`) - - }) - - It("HTTPRoute ResponseHeaderModifier", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", respHeaderModifyByHeaders, 1) - - By("access daataplane to check the HTTPRoute") - respExp := s.NewAPISIXClient(). - GET("/headers"). - WithHost("httpbin.example"). - Expect() - - respExp.Status(200) - respExp.Header("X-Resp-Add").IsEqual("add") - respExp.Header("X-Resp-Set").IsEqual("set") - respExp.Header("Server").IsEmpty() - respExp.Body(). - NotContains(`"X-Resp-Add": "add"`). - NotContains(`"X-Resp-Set": "set"`). - NotContains(`"Server"`) - }) - - It("HTTPRoute RequestRedirect", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", httpsRedirectByHeaders, 1) - - s.NewAPISIXClient().GET("/headers"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusFound). - Header("Location").IsEqual("https://httpbin.example:9443/headers") - - By("update HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", hostnameRedirectByHeaders, 2) - - s.NewAPISIXClient().GET("/headers"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusMovedPermanently). - Header("Location").IsEqual("http://httpbin.org/headers") - }) - - It("HTTPRoute RequestMirror", func() { - echoRoute := ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: echo -spec: - selector: - matchLabels: - app: echo - replicas: 1 - template: - metadata: - labels: - app: echo - spec: - containers: - - name: echo - image: jmalloc/echo-server:latest - ports: - - containerPort: 8080 ---- -apiVersion: v1 -kind: Service -metadata: - name: echo-service -spec: - selector: - app: echo - ports: - - name: http - port: 80 - protocol: TCP - targetPort: 8080 ---- -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /headers - filters: - - type: RequestMirror - requestMirror: - backendRef: - name: echo-service - port: 80 - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - ResourceApplied("HTTPRoute", "httpbin", echoRoute, 1) - - time.Sleep(time.Second * 6) - - _ = s.NewAPISIXClient().GET("/headers"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusOK) - - echoLogs := s.GetDeploymentLogs("echo") - Expect(echoLogs).To(ContainSubstring("GET /headers")) - }) - - It("HTTPRoute URLRewrite with ReplaceFullPath And Hostname", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", replaceFullPathAndHost, 1) - - By("/replace/201 should be rewritten to /headers") - s.NewAPISIXClient().GET("/replace/201"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusOK). - Body(). - Contains("replace.example.org") - - By("/replace/500 should be rewritten to /headers") - s.NewAPISIXClient().GET("/replace/500"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusOK). - Body(). - Contains("replace.example.org") - }) - - It("HTTPRoute URLRewrite with ReplacePrefixMatch", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", replacePrefixMatch, 1) - - By("/replace/201 should be rewritten to /status/201") - s.NewAPISIXClient().GET("/replace/201"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusCreated) - - By("/replace/500 should be rewritten to /status/500") - s.NewAPISIXClient().GET("/replace/500"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusInternalServerError) - }) - - It("HTTPRoute ExtensionRef", func() { - By("create HTTPRoute") - err := s.CreateResourceFromString(echoPlugin) - Expect(err).NotTo(HaveOccurred(), "creating PluginConfig") - ResourceApplied("HTTPRoute", "httpbin", extensionRefEchoPlugin, 1) - - s.NewAPISIXClient().GET("/get"). - WithHeader("Host", "httpbin.example"). - Expect(). - Body(). - Contains("Hello, World!!") - - err = s.CreateResourceFromString(echoPluginUpdated) - Expect(err).NotTo(HaveOccurred(), "updating PluginConfig") - time.Sleep(5 * time.Second) - - s.NewAPISIXClient().GET("/get"). - WithHeader("Host", "httpbin.example"). - Expect(). - Body(). - Contains("Updated") - }) - }) - - Context("HTTPRoute Multiple Backend", func() { - var sameWeiht = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 - weight: 50 - - name: nginx - port: 80 - weight: 50 - ` - var oneWeiht = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 - weight: 100 - - name: nginx - port: 80 - weight: 0 - ` - - BeforeEach(func() { - beforeEachHTTP() - s.DeployNginx(framework.NginxOptions{ - Namespace: s.Namespace(), - }) - }) - It("HTTPRoute Canary", func() { - ResourceApplied("HTTPRoute", "httpbin", sameWeiht, 1) - - var ( - hitNginxCnt = 0 - hitHttpbinCnt = 0 - ) - for i := 0; i < 100; i++ { - body := s.NewAPISIXClient().GET("/get"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusOK). - Body().Raw() - - if strings.Contains(body, "Hello") { - hitNginxCnt++ - } else { - hitHttpbinCnt++ - } - } - Expect(hitNginxCnt - hitHttpbinCnt).To(BeNumerically("~", 0, 2)) - - ResourceApplied("HTTPRoute", "httpbin", oneWeiht, 2) - - hitNginxCnt = 0 - hitHttpbinCnt = 0 - for i := 0; i < 100; i++ { - body := s.NewAPISIXClient().GET("/get"). - WithHeader("Host", "httpbin.example"). - Expect(). - Status(http.StatusOK). - Body().Raw() - - if strings.Contains(body, "Hello") { - hitNginxCnt++ - } else { - hitHttpbinCnt++ - } - } - Expect(hitHttpbinCnt - hitNginxCnt).To(Equal(100)) - }) - }) - - Context("HTTPRoute with GatewayProxy Update", func() { - var additionalGatewayGroupID string - - var exactRouteByGet = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: httpbin -spec: - parentRefs: - - name: apisix - hostnames: - - httpbin.example - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var updatedGatewayProxy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - BeforeEach(beforeEachHTTP) - - It("Should sync HTTPRoute when GatewayProxy is updated", func() { - By("create HTTPRoute") - ResourceApplied("HTTPRoute", "httpbin", exactRouteByGet, 1) - - By("verify HTTPRoute works") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - - By("create additional gateway group to get new admin key") - var err error - additionalGatewayGroupID, _, err = s.CreateAdditionalGatewayGroup("gateway-proxy-update") - Expect(err).NotTo(HaveOccurred(), "creating additional gateway group") - - resources, exists := s.GetAdditionalGatewayGroup(additionalGatewayGroupID) - Expect(exists).To(BeTrue(), "additional gateway group should exist") - - client, err := s.NewAPISIXClientForGatewayGroup(additionalGatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "creating APISIX client for additional gateway group") - - By("HTTPRoute not found for additional gateway group") - client. - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(404) - - By("update GatewayProxy with new admin key") - updatedProxy := fmt.Sprintf(updatedGatewayProxy, framework.DashboardTLSEndpoint, resources.AdminAPIKey) - err = s.CreateResourceFromString(updatedProxy) - Expect(err).NotTo(HaveOccurred(), "updating GatewayProxy") - time.Sleep(5 * time.Second) - - By("verify HTTPRoute works for additional gateway group") - client. - GET("/get"). - WithHost("httpbin.example"). - Expect(). - Status(200) - }) - }) - - /* - Context("HTTPRoute Status Updated", func() { - }) - - Context("HTTPRoute ParentRefs With Multiple Gateway", func() { - }) - - - Context("HTTPRoute BackendRefs Discovery", func() { - }) - */ -}) diff --git a/test/e2e/ingress/ingress.go b/test/e2e/ingress/ingress.go deleted file mode 100644 index fb0de348a..000000000 --- a/test/e2e/ingress/ingress.go +++ /dev/null @@ -1,1013 +0,0 @@ -// 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 ingress - -import ( - "context" - "encoding/base64" - "fmt" - "net/http" - "strings" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - - "github.com/apache/apisix-ingress-controller/api/v1alpha1" - "github.com/apache/apisix-ingress-controller/test/e2e/framework" - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -const _secretName = "test-ingress-tls" - -var Cert = strings.TrimSpace(framework.TestServerCert) - -var Key = strings.TrimSpace(framework.TestServerKey) - -func createSecret(s *scaffold.Scaffold, secretName string) { - err := s.NewKubeTlsSecret(secretName, Cert, Key) - assert.Nil(GinkgoT(), err, "create secret error") -} - -var _ = Describe("Test Ingress", func() { - s := scaffold.NewScaffold(&scaffold.Options{ - ControllerName: "apisix.apache.org/apisix-ingress-controller", - }) - - var gatewayProxyYaml = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config - namespace: default -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - Context("Ingress TLS", func() { - It("Check if SSL resource was created", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - - By("create GatewayProxy") - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - secretName := _secretName - host := "api6.com" - createSecret(s, secretName) - - var defaultIngressClass = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: "default" - scope: "Namespace" -` - - var tlsIngress = fmt.Sprintf(` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress-tls -spec: - ingressClassName: apisix - tls: - - hosts: - - %s - secretName: %s - rules: - - host: %s - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -`, host, secretName, host) - - By("create IngressClass") - err = s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") - Expect(err).NotTo(HaveOccurred(), "creating IngressClass") - time.Sleep(5 * time.Second) - - By("create Ingress with TLS") - err = s.CreateResourceFromString(tlsIngress) - Expect(err).NotTo(HaveOccurred(), "creating Ingress with TLS") - time.Sleep(5 * time.Second) - - By("check TLS configuration") - tls, err := s.DefaultDataplaneResource().SSL().List(context.Background()) - assert.Nil(GinkgoT(), err, "list tls error") - assert.Len(GinkgoT(), tls, 1, "tls number not expect") - assert.Equal(GinkgoT(), Cert, tls[0].Cert, "tls cert not expect") - assert.ElementsMatch(GinkgoT(), []string{host}, tls[0].Snis) - }) - }) - - Context("IngressClass Selection", func() { - var defaultIngressClass = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix-default - annotations: - ingressclass.kubernetes.io/is-default-class: "true" -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: "default" - scope: "Namespace" -` - - var defaultIngress = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress-default -spec: - rules: - - host: default.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - - var ingressWithExternalName = ` -apiVersion: v1 -kind: Service -metadata: - name: httpbin-external-domain -spec: - type: ExternalName - externalName: postman-echo.com ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress-external -spec: - rules: - - host: httpbin.external - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-external-domain - port: - number: 80 -` - - It("Test IngressClass Selection", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create Default IngressClass") - err = s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") - Expect(err).NotTo(HaveOccurred(), "creating Default IngressClass") - time.Sleep(5 * time.Second) - - By("create Ingress without IngressClass") - err = s.CreateResourceFromString(defaultIngress) - Expect(err).NotTo(HaveOccurred(), "creating Ingress without IngressClass") - time.Sleep(5 * time.Second) - - By("verify default ingress") - s.NewAPISIXClient(). - GET("/get"). - WithHost("default.example.com"). - Expect(). - Status(200) - }) - - It("Proxy External Service", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create Default IngressClass") - err = s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") - Expect(err).NotTo(HaveOccurred(), "creating Default IngressClass") - time.Sleep(5 * time.Second) - - By("create Ingress") - err = s.CreateResourceFromString(ingressWithExternalName) - Expect(err).NotTo(HaveOccurred(), "creating Ingress without IngressClass") - time.Sleep(5 * time.Second) - - By("checking the external service response") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.external"). - Expect(). - Status(200) - }) - - It("Delete Ingress during restart", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create Default IngressClass") - err = s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") - Expect(err).NotTo(HaveOccurred(), "creating Default IngressClass") - time.Sleep(5 * time.Second) - - By("create Ingress with ExternalName") - err = s.CreateResourceFromString(ingressWithExternalName) - Expect(err).NotTo(HaveOccurred(), "creating Ingress without IngressClass") - time.Sleep(5 * time.Second) - - By("create Ingress") - err = s.CreateResourceFromString(defaultIngress) - Expect(err).NotTo(HaveOccurred(), "creating Ingress without IngressClass") - time.Sleep(5 * time.Second) - - By("checking the external service response") - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.external"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("default.example.com"). - Expect(). - Status(200) - - s.ScaleIngress(0) - - By("delete Ingress") - err = s.DeleteResourceFromString(defaultIngress) - Expect(err).NotTo(HaveOccurred(), "deleting Ingress without IngressClass") - - s.ScaleIngress(1) - time.Sleep(1 * time.Minute) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("httpbin.external"). - Expect(). - Status(200) - - s.NewAPISIXClient(). - GET("/get"). - WithHost("default.example.com"). - Expect(). - Status(404) - }) - }) - - Context("IngressClass with GatewayProxy", func() { - gatewayProxyYaml := ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config - namespace: default -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: response-rewrite - enabled: true - config: - headers: - X-Proxy-Test: "enabled" -` - - gatewayProxyWithSecretYaml := ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config-with-secret - namespace: default -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - valueFrom: - secretKeyRef: - name: admin-secret - key: admin-key - plugins: - - name: response-rewrite - enabled: true - config: - headers: - X-Proxy-Test: "enabled" -` - - var ingressClassWithProxy = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix-with-proxy - annotations: - ingressclass.kubernetes.io/is-default-class: "true" -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: "default" - scope: "Namespace" -` - - var ingressClassWithProxySecret = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix-with-proxy-secret -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config-with-secret" - namespace: "default" - scope: "Namespace" -` - - var testIngress = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress-with-proxy -spec: - ingressClassName: apisix-with-proxy - rules: - - host: proxy.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - - var testIngressWithSecret = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress-with-proxy-secret -spec: - ingressClassName: apisix-with-proxy-secret - rules: - - host: proxy-secret.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - - It("Test IngressClass with GatewayProxy", func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - - By("create GatewayProxy") - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create IngressClass with GatewayProxy reference") - err = s.CreateResourceFromStringWithNamespace(ingressClassWithProxy, "") - Expect(err).NotTo(HaveOccurred(), "creating IngressClass with GatewayProxy") - time.Sleep(5 * time.Second) - - By("create Ingress with GatewayProxy IngressClass") - err = s.CreateResourceFromString(testIngress) - Expect(err).NotTo(HaveOccurred(), "creating Ingress with GatewayProxy IngressClass") - time.Sleep(5 * time.Second) - - By("verify HTTP request") - resp := s.NewAPISIXClient(). - GET("/get"). - WithHost("proxy.example.com"). - Expect(). - Status(200) - resp.Header("X-Proxy-Test").IsEqual("enabled") - }) - - It("Test IngressClass with GatewayProxy using Secret", func() { - By("create admin key secret") - adminSecret := fmt.Sprintf(` -apiVersion: v1 -kind: Secret -metadata: - name: admin-secret - namespace: default -type: Opaque -stringData: - admin-key: %s -`, s.AdminKey()) - err := s.CreateResourceFromStringWithNamespace(adminSecret, "default") - Expect(err).NotTo(HaveOccurred(), "creating admin secret") - time.Sleep(5 * time.Second) - - By("create GatewayProxy with Secret reference") - gatewayProxy := fmt.Sprintf(gatewayProxyWithSecretYaml, framework.DashboardTLSEndpoint) - err = s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy with Secret") - time.Sleep(5 * time.Second) - - By("create IngressClass with GatewayProxy reference") - err = s.CreateResourceFromStringWithNamespace(ingressClassWithProxySecret, "") - Expect(err).NotTo(HaveOccurred(), "creating IngressClass with GatewayProxy") - time.Sleep(5 * time.Second) - - By("create Ingress with GatewayProxy IngressClass") - err = s.CreateResourceFromString(testIngressWithSecret) - Expect(err).NotTo(HaveOccurred(), "creating Ingress with GatewayProxy IngressClass") - time.Sleep(5 * time.Second) - - By("verify HTTP request") - resp := s.NewAPISIXClient(). - GET("/get"). - WithHost("proxy-secret.example.com"). - Expect(). - Status(200) - resp.Header("X-Proxy-Test").IsEqual("enabled") - }) - }) - - Context("HTTPRoutePolicy for Ingress", func() { - getGatewayProxySpec := func() string { - return fmt.Sprintf(` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config - namespace: default -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -`, framework.DashboardTLSEndpoint, s.AdminKey()) - } - - const ingressClassSpec = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: "default" - scope: "Namespace" -` - const ingressSpec = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: default -spec: - ingressClassName: apisix - rules: - - host: example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - const httpRoutePolicySpec0 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-0 -spec: - targetRefs: - - group: networking.k8s.io - kind: Ingress - name: default - priority: 10 - vars: - - - http_x_hrp_name - - == - - http-route-policy-0 -` - const httpRoutePolicySpec1 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-0 -spec: - targetRefs: - - group: networking.k8s.io - kind: Ingress - name: default - priority: 10 - vars: - - - arg_hrp_name - - == - - http-route-policy-0 -` - const httpRoutePolicySpec2 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-0 -spec: - targetRefs: - - group: networking.k8s.io - kind: Ingress - name: other - priority: 10 - vars: - - - arg_hrp_name - - == - - http-route-policy-0 -` - const httpRoutePolicySpec3 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: HTTPRoutePolicy -metadata: - name: http-route-policy-1 -spec: - targetRefs: - - group: networking.k8s.io - kind: Ingress - name: default - priority: 20 - vars: - - - arg_hrp_name - - == - - http-route-policy-0 -` - BeforeEach(func() { - By("create GatewayProxy") - err := s.CreateResourceFromStringWithNamespace(getGatewayProxySpec(), "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create IngressClass") - err = s.CreateResourceFromStringWithNamespace(ingressClassSpec, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - }) - - It("HTTPRoutePolicy targetRef an Ingress", func() { - By("create Ingress") - err := s.CreateResourceFromString(ingressSpec) - Expect(err).NotTo(HaveOccurred(), "creating Ingress") - - By("request the route should be OK") - Eventually(func() int { - return s.NewAPISIXClient().GET("/get").WithHost("example.com").Expect().Raw().StatusCode - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) - - By("create HTTPRoutePolicy") - err = s.CreateResourceFromString(httpRoutePolicySpec0) - Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy") - Eventually(func() string { - spec, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-0") - Expect(err).NotTo(HaveOccurred(), "HTTPRoutePolicy status should be True") - return spec - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring(`status: "True"`)) - - By("request the route without vars should be Not Found") - Eventually(func() int { - return s.NewAPISIXClient().GET("/get").WithHost("example.com").Expect().Raw().StatusCode - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) - - By("request the route with the correct vars should be OK") - s.NewAPISIXClient().GET("/get").WithHost("example.com"). - WithHeader("X-HRP-Name", "http-route-policy-0").Expect().Status(http.StatusOK) - - By("update the HTTPRoutePolicy") - err = s.CreateResourceFromString(httpRoutePolicySpec1) - Expect(err).NotTo(HaveOccurred(), "updating HTTPRoutePolicy") - - By("request with the old vars should be Not Found") - Eventually(func() int { - return s.NewAPISIXClient().GET("/get").WithHost("example.com"). - WithHeader("X-HRP-Name", "http-route-policy-0").Expect().Raw().StatusCode - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) - - By("request with the new vars should be OK") - s.NewAPISIXClient().GET("/get").WithHost("example.com"). - WithQuery("hrp_name", "http-route-policy-0").Expect().Status(http.StatusOK) - - By("update the HTTPRoutePolicy's targetRef") - err = s.CreateResourceFromString(httpRoutePolicySpec2) - Expect(err).NotTo(HaveOccurred(), "updating HTTPRoutePolicy") - - By("request the route without vars should be OK") - Eventually(func() int { - return s.NewAPISIXClient().GET("/get").WithHost("example.com").Expect().Raw().StatusCode - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) - - By("revert the HTTPRoutePolicy") - err = s.CreateResourceFromString(httpRoutePolicySpec0) - Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy") - - By("request the route without vars should be Not Found") - Eventually(func() int { - return s.NewAPISIXClient().GET("/get").WithHost("example.com").Expect().Raw().StatusCode - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) - - By("request the route with the correct vars should be OK") - s.NewAPISIXClient().GET("/get").WithHost("example.com"). - WithHeader("X-HRP-Name", "http-route-policy-0").Expect().Status(http.StatusOK) - - By("apply conflict HTTPRoutePolicy") - err = s.CreateResourceFromString(httpRoutePolicySpec3) - Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy") - Eventually(func() string { - spec, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-1") - Expect(err).NotTo(HaveOccurred(), "get HTTPRoutePolicy") - return spec - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring("reason: Conflicted")) - - By("delete the HTTPRoutePolicy") - for _, name := range []string{"http-route-policy-0", "http-route-policy-1"} { - err = s.DeleteResource("HTTPRoutePolicy", name) - Expect(err).NotTo(HaveOccurred(), "deleting HTTPRoutePolicy") - } - - By("request the route without vars should be OK") - Eventually(func() int { - return s.NewAPISIXClient().GET("/get").WithHost("example.com").Expect().Raw().StatusCode - }). - WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) - }) - - It("HTTPRoutePolicy status changes on Ingress deleting", func() { - By("create Ingress") - err := s.CreateResourceFromString(ingressSpec) - Expect(err).NotTo(HaveOccurred(), "creating Ingress") - - By("create HTTPRoutePolicy") - err = s.CreateResourceFromString(httpRoutePolicySpec0) - Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy") - framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 8*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"}, - types.NamespacedName{Namespace: s.Namespace(), Name: "http-route-policy-0"}, - metav1.Condition{ - Type: string(gatewayv1alpha2.PolicyConditionAccepted), - Status: metav1.ConditionTrue, - Reason: string(gatewayv1alpha2.PolicyReasonAccepted), - }, - ) - - By("delete ingress") - err = s.DeleteResource("Ingress", "default") - Expect(err).NotTo(HaveOccurred(), "delete Ingress") - - err = framework.EventuallyHTTPRoutePolicyHaveStatus(s.K8sClient, 8*time.Second, - types.NamespacedName{Namespace: s.Namespace(), Name: "http-route-policy-0"}, - func(_ v1alpha1.HTTPRoutePolicy, status v1alpha1.PolicyStatus) bool { - return len(status.Ancestors) == 0 - }, - ) - Expect(err).NotTo(HaveOccurred(), "expected HTTPRoutePolicy.Status has no Ancestor") - }) - }) - - Context("Ingress with GatewayProxy Update", func() { - var additionalGatewayGroupID string - - var ingressClass = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix-ingress-class -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: "default" - scope: "Namespace" -` - var ingress = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress -spec: - ingressClassName: apisix-ingress-class - rules: - - host: ingress.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - var updatedGatewayProxy = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config - namespace: default -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" -` - - BeforeEach(func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create IngressClass") - err = s.CreateResourceFromStringWithNamespace(ingressClass, "") - Expect(err).NotTo(HaveOccurred(), "creating IngressClass") - time.Sleep(5 * time.Second) - }) - - It("Should sync Ingress when GatewayProxy is updated", func() { - By("create Ingress") - err := s.CreateResourceFromString(ingress) - Expect(err).NotTo(HaveOccurred(), "creating Ingress") - time.Sleep(5 * time.Second) - - By("verify Ingress works") - s.NewAPISIXClient(). - GET("/get"). - WithHost("ingress.example.com"). - Expect(). - Status(200) - - By("create additional gateway group to get new admin key") - additionalGatewayGroupID, _, err = s.CreateAdditionalGatewayGroup("gateway-proxy-update") - Expect(err).NotTo(HaveOccurred(), "creating additional gateway group") - - client, err := s.NewAPISIXClientForGatewayGroup(additionalGatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "creating APISIX client for additional gateway group") - - By("Ingress not found for additional gateway group") - client. - GET("/get"). - WithHost("ingress.example.com"). - Expect(). - Status(404) - - resources, exists := s.GetAdditionalGatewayGroup(additionalGatewayGroupID) - Expect(exists).To(BeTrue(), "additional gateway group should exist") - - By("update GatewayProxy with new admin key") - updatedProxy := fmt.Sprintf(updatedGatewayProxy, framework.DashboardTLSEndpoint, resources.AdminAPIKey) - err = s.CreateResourceFromStringWithNamespace(updatedProxy, "default") - Expect(err).NotTo(HaveOccurred(), "updating GatewayProxy") - time.Sleep(5 * time.Second) - - By("verify Ingress works for additional gateway group") - client. - GET("/get"). - WithHost("ingress.example.com"). - Expect(). - Status(200) - }) - }) - - Context("GatewayProxy reference Secret", func() { - const secretSpec = ` -apiVersion: v1 -kind: Secret -metadata: - name: control-plane-secret -data: - admin-key: %s -` - const gatewayProxySpec = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - valueFrom: - secretKeyRef: - name: control-plane-secret - key: admin-key - plugins: - - name: response-rewrite - enabled: true - config: - headers: - X-Proxy-Test: "enabled" -` - const ingressClassSpec = ` -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: apisix-ingress-class -spec: - controller: "apisix.apache.org/apisix-ingress-controller" - parameters: - apiGroup: "apisix.apache.org" - kind: "GatewayProxy" - name: "apisix-proxy-config" - namespace: %s - scope: "Namespace" -` - const ingressSpec = ` -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: apisix-ingress -spec: - ingressClassName: apisix-ingress-class - rules: - - host: ingress.example.com - http: - paths: - - path: /get - pathType: Prefix - backend: - service: - name: httpbin-service-e2e-test - port: - number: 80 -` - var ( - additionalGatewayGroupID string - err error - ) - - It("GatewayProxy reference Secret", func() { - By("create Secret") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(secretSpec, base64.StdEncoding.EncodeToString([]byte(s.AdminKey()))), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating secret") - - By("create GatewayProxy") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayProxySpec, framework.DashboardTLSEndpoint), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating gateway proxy") - - By("create IngressClass") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressClassSpec, s.Namespace()), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating IngressClass") - - By("creat Ingress") - err = s.CreateResourceFromStringWithNamespace(ingressSpec, s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Ingress") - - By("verify Ingress works") - Eventually(func() int { - return s.NewAPISIXClient(). - GET("/get"). - WithHost("ingress.example.com"). - Expect().Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second). - Should(Equal(http.StatusOK)) - s.NewAPISIXClient(). - GET("/get"). - WithHost("ingress.example.com"). - Expect().Header("X-Proxy-Test").IsEqual("enabled") - - By("create additional gateway group to get new admin key") - additionalGatewayGroupID, _, err = s.CreateAdditionalGatewayGroup("gateway-proxy-update") - Expect(err).NotTo(HaveOccurred(), "creating additional gateway group") - - client, err := s.NewAPISIXClientForGatewayGroup(additionalGatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "creating APISIX client for additional gateway group") - - By("Ingress not found for additional gateway group") - client. - GET("/get"). - WithHost("ingress.example.com"). - Expect(). - Status(http.StatusNotFound) - - resources, exists := s.GetAdditionalGatewayGroup(additionalGatewayGroupID) - Expect(exists).To(BeTrue(), "additional gateway group should exist") - - By("update secret") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(secretSpec, base64.StdEncoding.EncodeToString([]byte(resources.AdminAPIKey))), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating secret") - - By("verify Ingress works for additional gateway group") - Eventually(func() int { - return client. - GET("/get"). - WithHost("ingress.example.com"). - Expect().Raw().StatusCode - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second). - Should(Equal(http.StatusOK)) - Eventually(func() string { - return client. - GET("/get"). - WithHost("ingress.example.com"). - Expect().Raw().Header.Get("X-Proxy-Test") - }).WithTimeout(8 * time.Second).ProbeEvery(time.Second). - Should(Equal("enabled")) - }) - }) -}) diff --git a/test/e2e/scaffold/dp.go b/test/e2e/scaffold/dp.go deleted file mode 100644 index 3cd39bb78..000000000 --- a/test/e2e/scaffold/dp.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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 scaffold - -import ( - . "github.com/onsi/gomega" - - "github.com/apache/apisix-ingress-controller/test/e2e/framework" -) - -func (s *Scaffold) deployDataplane() { - svc := s.DeployGateway(framework.DataPlaneDeployOptions{ - GatewayGroupID: s.gatewaygroupid, - Namespace: s.namespace, - Name: "api7ee3-apisix-gateway-mtls", - DPManagerEndpoint: framework.DPManagerTLSEndpoint, - SetEnv: true, - SSLKey: framework.TestKey, - SSLCert: framework.TestCert, - TLSEnabled: true, - ForIngressGatewayGroup: true, - ServiceHTTPPort: 9080, - ServiceHTTPSPort: 9443, - }) - - s.dataplaneService = svc - - err := s.newAPISIXTunnels() - Expect(err).ToNot(HaveOccurred(), "creating apisix tunnels") -} - -func (s *Scaffold) newAPISIXTunnels() error { - serviceName := "api7ee3-apisix-gateway-mtls" - httpTunnel, httpsTunnel, err := s.createDataplaneTunnels(s.dataplaneService, s.kubectlOptions, serviceName) - if err != nil { - return err - } - - s.apisixHttpTunnel = httpTunnel - s.apisixHttpsTunnel = httpsTunnel - return nil -} diff --git a/test/e2e/scaffold/httpbin.go b/test/e2e/scaffold/httpbin.go deleted file mode 100644 index 6926aa1ae..000000000 --- a/test/e2e/scaffold/httpbin.go +++ /dev/null @@ -1,169 +0,0 @@ -// 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 scaffold - -import ( - "fmt" - "time" - - "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/onsi/ginkgo/v2" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -var ( - _httpbinDeploymentTemplate = ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: httpbin-deployment-e2e-test -spec: - replicas: %d - selector: - matchLabels: - app: httpbin-deployment-e2e-test - strategy: - rollingUpdate: - maxSurge: 50%% - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: httpbin-deployment-e2e-test - spec: - terminationGracePeriodSeconds: 0 - containers: - - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 2 - periodSeconds: 5 - successThreshold: 1 - tcpSocket: - port: 80 - timeoutSeconds: 2 - readinessProbe: - failureThreshold: 3 - initialDelaySeconds: 2 - periodSeconds: 5 - successThreshold: 1 - tcpSocket: - port: 80 - timeoutSeconds: 2 - image: "kennethreitz/httpbin:latest" - imagePullPolicy: IfNotPresent - name: httpbin-deployment-e2e-test - ports: - - containerPort: 80 - name: "http" - protocol: "TCP" -` - _httpService = ` -apiVersion: v1 -kind: Service -metadata: - name: httpbin-service-e2e-test -spec: - selector: - app: httpbin-deployment-e2e-test - ports: - - name: http - port: 80 - protocol: TCP - targetPort: 80 - type: ClusterIP -` -) - -func (s *Scaffold) newHTTPBIN() (*corev1.Service, error) { - httpbinDeployment := fmt.Sprintf(s.FormatRegistry(_httpbinDeploymentTemplate), 1) - if err := s.CreateResourceFromString(httpbinDeployment); err != nil { - return nil, err - } - if err := s.CreateResourceFromString(_httpService); err != nil { - return nil, err - } - svc, err := k8s.GetServiceE(s.t, s.kubectlOptions, "httpbin-service-e2e-test") - if err != nil { - return nil, err - } - return svc, nil -} - -func (s *Scaffold) NewHTTPBINWithNamespace(namespace string) (*corev1.Service, error) { - originalNamespace := s.kubectlOptions.Namespace - s.kubectlOptions.Namespace = namespace - defer func() { - s.kubectlOptions.Namespace = originalNamespace - }() - return s.newHTTPBIN() -} - -// ScaleHTTPBIN scales the number of HTTPBIN pods to desired. -func (s *Scaffold) ScaleHTTPBIN(desired int) error { - httpbinDeployment := fmt.Sprintf(s.FormatRegistry(_httpbinDeploymentTemplate), desired) - if err := s.CreateResourceFromString(httpbinDeployment); err != nil { - return err - } - if err := k8s.WaitUntilNumPodsCreatedE( - s.t, - s.kubectlOptions, - s.labelSelector("app=httpbin-deployment-e2e-test"), - desired, - 5, - 5*time.Second, - ); err != nil { - return err - } - return nil -} - -// DeleteHTTPBINService deletes the HTTPBIN service object. -func (s *Scaffold) DeleteHTTPBINService() error { - return k8s.KubectlDeleteFromStringE(s.t, s.kubectlOptions, _httpService) -} - -// WaitAllHTTPBINPodsAvailable waits until all httpbin pods ready. -func (s *Scaffold) WaitAllHTTPBINPodsAvailable() error { - opts := metav1.ListOptions{ - LabelSelector: "app=httpbin-deployment-e2e-test", - } - condFunc := func() (bool, error) { - items, err := k8s.ListPodsE(s.t, s.kubectlOptions, opts) - if err != nil { - return false, err - } - if len(items) == 0 { - ginkgo.GinkgoT().Log("no apisix pods created") - return false, nil - } - for _, item := range items { - foundPodReady := false - for _, cond := range item.Status.Conditions { - if cond.Type != corev1.PodReady { - continue - } - foundPodReady = true - if cond.Status != "True" { - return false, nil - } - } - if !foundPodReady { - return false, nil - } - } - return true, nil - } - return waitExponentialBackoff(condFunc) -} diff --git a/test/e2e/scaffold/ingress.go b/test/e2e/scaffold/ingress.go deleted file mode 100644 index 248c0e26b..000000000 --- a/test/e2e/scaffold/ingress.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 scaffold - -import ( - "github.com/apache/apisix-ingress-controller/test/e2e/framework" -) - -func (s *Scaffold) deployIngress() { - s.DeployIngress(framework.IngressDeployOpts{ - ControllerName: s.opts.ControllerName, - AdminKey: s.AdminKey(), - AdminTLSVerify: false, - Namespace: s.namespace, - AdminEnpoint: framework.DashboardTLSEndpoint, - Replicas: 1, - }) -} - -func (s *Scaffold) ScaleIngress(replicas int) { - s.DeployIngress(framework.IngressDeployOpts{ - ControllerName: s.opts.ControllerName, - AdminKey: s.AdminKey(), - AdminTLSVerify: false, - Namespace: s.namespace, - AdminEnpoint: framework.DashboardTLSEndpoint, - Replicas: replicas, - }) -} diff --git a/test/e2e/scaffold/k8s.go b/test/e2e/scaffold/k8s.go deleted file mode 100644 index eb29bd259..000000000 --- a/test/e2e/scaffold/k8s.go +++ /dev/null @@ -1,283 +0,0 @@ -// 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 scaffold - -import ( - "context" - "fmt" - "net/url" - "os/exec" - "strings" - "time" - - "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/gruntwork-io/terratest/modules/retry" - "github.com/gruntwork-io/terratest/modules/testing" - . "github.com/onsi/ginkgo/v2" //nolint:staticcheck - . "github.com/onsi/gomega" //nolint:staticcheck - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - - "github.com/apache/apisix-ingress-controller/pkg/dashboard" - "github.com/apache/apisix-ingress-controller/test/e2e/framework" -) - -// CreateResourceFromString creates resource from a loaded yaml string. -func (s *Scaffold) CreateResourceFromString(yaml string) error { - return k8s.KubectlApplyFromStringE(s.t, s.kubectlOptions, yaml) -} - -func (s *Scaffold) DeleteResourceFromString(yaml string) error { - return k8s.KubectlDeleteFromStringE(s.t, s.kubectlOptions, yaml) -} - -func (s *Scaffold) Exec(podName, containerName string, args ...string) (string, error) { - cmdArgs := []string{} - - if s.kubectlOptions.ContextName != "" { - cmdArgs = append(cmdArgs, "--context", s.kubectlOptions.ContextName) - } - if s.kubectlOptions.ConfigPath != "" { - cmdArgs = append(cmdArgs, "--kubeconfig", s.kubectlOptions.ConfigPath) - } - if s.kubectlOptions.Namespace != "" { - cmdArgs = append(cmdArgs, "--namespace", s.kubectlOptions.Namespace) - } - - cmdArgs = append(cmdArgs, "exec") - cmdArgs = append(cmdArgs, "-i") - cmdArgs = append(cmdArgs, podName) - cmdArgs = append(cmdArgs, "-c") - cmdArgs = append(cmdArgs, containerName) - cmdArgs = append(cmdArgs, "--", "sh", "-c") - cmdArgs = append(cmdArgs, args...) - - GinkgoWriter.Printf("running command: kubectl %v\n", strings.Join(cmdArgs, " ")) - - output, err := exec.Command("kubectl", cmdArgs...).Output() - - return strings.TrimSuffix(string(output), "\n"), err -} - -func (s *Scaffold) GetOutputFromString(shell ...string) (string, error) { - cmdArgs := []string{} - cmdArgs = append(cmdArgs, "get") - cmdArgs = append(cmdArgs, shell...) - output, err := k8s.RunKubectlAndGetOutputE(GinkgoT(), s.kubectlOptions, cmdArgs...) - return output, err -} - -func (s *Scaffold) GetResourceYamlFromNamespace(resourceType, resourceName, namespace string) (string, error) { - return s.GetOutputFromString(resourceType, resourceName, "-n", namespace, "-o", "yaml") -} - -func (s *Scaffold) GetResourceYaml(resourceType, resourceName string) (string, error) { - return s.GetOutputFromString(resourceType, resourceName, "-o", "yaml") -} - -// RemoveResourceByString remove resource from a loaded yaml string. -func (s *Scaffold) RemoveResourceByString(yaml string) error { - err := k8s.KubectlDeleteFromStringE(s.t, s.kubectlOptions, yaml) - time.Sleep(5 * time.Second) - return err -} - -func (s *Scaffold) GetServiceByName(name string) (*corev1.Service, error) { - return k8s.GetServiceE(s.t, s.kubectlOptions, name) -} - -// ListPodsByLabels lists all pods which matching the label selector. -func (s *Scaffold) ListPodsByLabels(labels string) ([]corev1.Pod, error) { - return k8s.ListPodsE(s.t, s.kubectlOptions, metav1.ListOptions{ - LabelSelector: labels, - }) -} - -// CreateResourceFromStringWithNamespace creates resource from a loaded yaml string -// and sets its namespace to the specified one. -func (s *Scaffold) CreateResourceFromStringWithNamespace(yaml, namespace string) error { - originalNamespace := s.kubectlOptions.Namespace - s.kubectlOptions.Namespace = namespace - defer func() { - s.kubectlOptions.Namespace = originalNamespace - }() - s.addFinalizers(func() { - _ = s.DeleteResourceFromStringWithNamespace(yaml, namespace) - }) - return s.CreateResourceFromString(yaml) -} - -func (s *Scaffold) DeleteResourceFromStringWithNamespace(yaml, namespace string) error { - originalNamespace := s.kubectlOptions.Namespace - s.kubectlOptions.Namespace = namespace - defer func() { - s.kubectlOptions.Namespace = originalNamespace - }() - return k8s.KubectlDeleteFromStringE(s.t, s.kubectlOptions, yaml) -} - -func (s *Scaffold) NewAPISIX() (dashboard.Dashboard, error) { - return dashboard.NewClient() -} - -func (s *Scaffold) ClusterClient() (dashboard.Cluster, error) { - u := url.URL{ - Scheme: "http", - Host: "localhost:7080", - Path: "/apisix/admin", - } - cli, err := s.NewAPISIX() - if err != nil { - return nil, err - } - err = cli.AddCluster(context.Background(), &dashboard.ClusterOptions{ - BaseURL: u.String(), - ControllerName: s.opts.ControllerName, - Labels: map[string]string{"k8s/controller-name": s.opts.ControllerName}, - AdminKey: s.opts.APISIXAdminAPIKey, - SyncCache: true, - }) - if err != nil { - return nil, err - } - return cli.Cluster(""), nil -} - -func (s *Scaffold) shutdownApisixTunnel() { - s.apisixHttpTunnel.Close() - s.apisixHttpsTunnel.Close() -} - -// Namespace returns the current working namespace. -func (s *Scaffold) Namespace() string { - return s.kubectlOptions.Namespace -} - -func (s *Scaffold) EnsureNumEndpointsReady(t testing.TestingT, endpointsName string, desired int) { - e, err := k8s.GetKubernetesClientFromOptionsE(t, s.kubectlOptions) - Expect(err).ToNot(HaveOccurred(), "Getting Kubernetes clientset") - - statusMsg := fmt.Sprintf("Wait for endpoints %s to be ready.", endpointsName) - message := retry.DoWithRetry( - t, - statusMsg, - 20, - 2*time.Second, - func() (string, error) { - endpoints, err := e.CoreV1().Endpoints(s.Namespace()).Get(context.Background(), endpointsName, metav1.GetOptions{}) - if err != nil { - return "", err - } - readyNum := 0 - for _, subset := range endpoints.Subsets { - readyNum += len(subset.Addresses) - } - if readyNum == desired { - return "Service is now available", nil - } - return "failed", fmt.Errorf("endpoints not ready yet, expect %v, actual %v", desired, readyNum) - }, - ) - GinkgoT().Log(message) -} - -// GetKubernetesClient get kubernetes client use by scaffold -func (s *Scaffold) GetKubernetesClient() *kubernetes.Clientset { - client, err := k8s.GetKubernetesClientFromOptionsE(s.t, s.kubectlOptions) - Expect(err).ToNot(HaveOccurred(), "Getting Kubernetes clientset") - return client -} - -func (s *Scaffold) RunKubectlAndGetOutput(args ...string) (string, error) { - return k8s.RunKubectlAndGetOutputE(GinkgoT(), s.kubectlOptions, args...) -} - -func (s *Scaffold) RunDigDNSClientFromK8s(args ...string) (string, error) { - kubectlArgs := []string{ - "run", - "dig", - "-i", - "--rm", - "--restart=Never", - "--image-pull-policy=IfNotPresent", - "--image=toolbelt/dig", - "--", - } - kubectlArgs = append(kubectlArgs, args...) - return s.RunKubectlAndGetOutput(kubectlArgs...) -} - -func (s *Scaffold) ResourceApplied(resourType, resourceName, resourceRaw string, observedGeneration int) { - Expect(s.CreateResourceFromString(resourceRaw)). - NotTo(HaveOccurred(), fmt.Sprintf("creating %s", resourType)) - - Eventually(func() string { - hryaml, err := s.GetResourceYaml(resourType, resourceName) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("getting %s yaml", resourType)) - return hryaml - }).WithTimeout(8*time.Second).ProbeEvery(2*time.Second). - Should( - SatisfyAll( - ContainSubstring(`status: "True"`), - ContainSubstring(fmt.Sprintf("observedGeneration: %d", observedGeneration)), - ), - fmt.Sprintf("checking %s condition status", resourType), - ) - time.Sleep(1 * time.Second) -} - -func (s *Scaffold) ApplyDefaultGatewayResource( - defaultGatewayProxy string, - defaultGatewayClass string, - defaultGateway string, - defaultHTTPRoute string, -) { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(defaultGatewayProxy, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create GatewayClass") - gatewayClassName := fmt.Sprintf("apisix-%d", time.Now().Unix()) - gatewayString := fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()) - err = s.CreateResourceFromStringWithNamespace(gatewayString, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("check GatewayClass condition") - gcyaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To( - ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), - "checking GatewayClass condition message", - ) - - By("create Gateway") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGateway, gatewayClassName), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To( - ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), - "checking Gateway condition message", - ) - - s.ResourceApplied("httproute", "httpbin", defaultHTTPRoute, 1) -} diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go deleted file mode 100644 index 9d7f72ea3..000000000 --- a/test/e2e/scaffold/scaffold.go +++ /dev/null @@ -1,814 +0,0 @@ -// 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 scaffold - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "net" - "net/http" - "net/url" - "os" - "os/user" - "path/filepath" - "strings" - "time" - - "github.com/gavv/httpexpect/v2" - "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/gruntwork-io/terratest/modules/testing" - . "github.com/onsi/ginkgo/v2" //nolint:staticcheck - . "github.com/onsi/gomega" //nolint:staticcheck - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - - "github.com/apache/apisix-ingress-controller/pkg/dashboard" - "github.com/apache/apisix-ingress-controller/pkg/utils" - "github.com/apache/apisix-ingress-controller/test/e2e/framework" -) - -const ( - DashboardHost = "localhost" - DashboardPort = 7080 - - DefaultControllerName = "apisix.apache.org/apisix-ingress-controller" -) - -type Options struct { - Name string - Kubeconfig string - APISIXAdminAPIVersion string - APISIXConfigPath string - IngressAPISIXReplicas int - HTTPBinServicePort int - APISIXAdminAPIKey string - EnableWebhooks bool - APISIXPublishAddress string - ApisixResourceSyncInterval string - ApisixResourceSyncComparison string - ApisixResourceVersion string - DisableStatus bool - IngressClass string - EnableEtcdServer bool - ControllerName string - - NamespaceSelectorLabel map[string][]string - DisableNamespaceSelector bool - DisableNamespaceLabel bool -} - -type Scaffold struct { - *framework.Framework - - opts *Options - kubectlOptions *k8s.KubectlOptions - namespace string - t testing.TestingT - nodes []corev1.Node - dataplaneService *corev1.Service - httpbinService *corev1.Service - - finalizers []func() - label map[string]string - - apisixCli dashboard.Dashboard - - gatewaygroupid string - apisixHttpTunnel *k8s.Tunnel - apisixHttpsTunnel *k8s.Tunnel - apisixTCPTunnel *k8s.Tunnel - apisixTLSOverTCPTunnel *k8s.Tunnel - apisixUDPTunnel *k8s.Tunnel - // apisixControlTunnel *k8s.Tunnel - - // Support for multiple Gateway groups - additionalGatewayGroups map[string]*GatewayGroupResources -} - -// GatewayGroupResources contains resources associated with a specific Gateway group -type GatewayGroupResources struct { - GatewayGroupID string - Namespace string - DataplaneService *corev1.Service - HttpTunnel *k8s.Tunnel - HttpsTunnel *k8s.Tunnel - AdminAPIKey string -} - -func (s *Scaffold) AdminKey() string { - return s.opts.APISIXAdminAPIKey -} - -// GetKubeconfig returns the kubeconfig file path. -// Order: -// env KUBECONFIG; -// ~/.kube/config; -// "" (in case in-cluster configuration will be used). -func GetKubeconfig() string { - kubeconfig := os.Getenv("KUBECONFIG") - if kubeconfig == "" { - u, err := user.Current() - if err != nil { - panic(err) - } - kubeconfig = filepath.Join(u.HomeDir, ".kube", "config") - if _, err := os.Stat(kubeconfig); err != nil && !os.IsNotExist(err) { - kubeconfig = "" - } - } - return kubeconfig -} - -// NewScaffold creates an e2e test scaffold. -func NewScaffold(o *Options) *Scaffold { - if o.Name == "" { - o.Name = "default" - } - if o.IngressAPISIXReplicas <= 0 { - o.IngressAPISIXReplicas = 1 - } - if o.Kubeconfig == "" { - o.Kubeconfig = GetKubeconfig() - } - if o.APISIXAdminAPIVersion == "" { - adminVersion := os.Getenv("APISIX_ADMIN_API_VERSION") - if adminVersion != "" { - o.APISIXAdminAPIVersion = adminVersion - } else { - o.APISIXAdminAPIVersion = "v3" - } - } - if enabled := os.Getenv("ENABLED_ETCD_SERVER"); enabled == "true" { - o.EnableEtcdServer = true - } - - if o.HTTPBinServicePort == 0 { - o.HTTPBinServicePort = 80 - } - defer GinkgoRecover() - - s := &Scaffold{ - Framework: framework.GetFramework(), - opts: o, - t: GinkgoT(), - } - - BeforeEach(s.beforeEach) - AfterEach(s.afterEach) - - return s -} - -// NewDefaultScaffold creates a scaffold with some default options. -// apisix-version default v2 -func NewDefaultScaffold() *Scaffold { - return NewScaffold(&Options{}) -} - -// KillPod kill the pod which name is podName. -func (s *Scaffold) KillPod(podName string) error { - cli, err := k8s.GetKubernetesClientE(s.t) - if err != nil { - return err - } - return cli.CoreV1().Pods(s.namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{}) -} - -// DefaultHTTPBackend returns the service name and service ports -// of the default http backend. -func (s *Scaffold) DefaultHTTPBackend() (string, []int32) { - ports := make([]int32, 0, len(s.httpbinService.Spec.Ports)) - for _, p := range s.httpbinService.Spec.Ports { - ports = append(ports, p.Port) - } - return s.httpbinService.Name, ports -} - -// ApisixAdminServiceAndPort returns the dashboard host and port -// func (s *Scaffold) ApisixAdminServiceAndPort() (string, int32) { -// return "apisix-service-e2e-test", 7080 -// } - -// NewAPISIXClient creates the default HTTP client. -func (s *Scaffold) NewAPISIXClient() *httpexpect.Expect { - u := url.URL{ - Scheme: "http", - Host: s.apisixHttpTunnel.Endpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{}, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(GinkgoT()), - ), - }) -} - -// GetAPISIXHTTPSEndpoint get apisix https endpoint from tunnel map -func (s *Scaffold) GetAPISIXHTTPSEndpoint() string { - return s.apisixHttpsTunnel.Endpoint() -} - -// NewAPISIXClientWithTCPProxy creates the HTTP client but with the TCP proxy of APISIX. -func (s *Scaffold) NewAPISIXClientWithTCPProxy() *httpexpect.Expect { - u := url.URL{ - Scheme: "http", - Host: s.apisixTCPTunnel.Endpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{}, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(GinkgoT()), - ), - }) -} - -func (s *Scaffold) DNSResolver() *net.Resolver { - return &net.Resolver{ - PreferGo: false, - Dial: func(ctx context.Context, network, address string) (net.Conn, error) { - d := net.Dialer{ - Timeout: time.Millisecond * time.Duration(10000), - } - return d.DialContext(ctx, "udp", s.apisixUDPTunnel.Endpoint()) - }, - } -} - -func (s *Scaffold) DialTLSOverTcp(serverName string) (*tls.Conn, error) { - return tls.Dial("tcp", s.apisixTLSOverTCPTunnel.Endpoint(), &tls.Config{ - InsecureSkipVerify: true, - ServerName: serverName, - }) -} - -func (s *Scaffold) UpdateNamespace(ns string) { - s.kubectlOptions.Namespace = ns -} - -// NewAPISIXHttpsClient creates the default HTTPS client. -func (s *Scaffold) NewAPISIXHttpsClient(host string) *httpexpect.Expect { - u := url.URL{ - Scheme: "https", - Host: s.apisixHttpsTunnel.Endpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - // accept any certificate; for testing only! - InsecureSkipVerify: true, - ServerName: host, - }, - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(GinkgoT()), - ), - }) -} - -// NewAPISIXHttpsClientWithCertificates creates the default HTTPS client with giving trusted CA and client certs. -func (s *Scaffold) NewAPISIXHttpsClientWithCertificates( - host string, insecure bool, ca *x509.CertPool, certs []tls.Certificate, -) *httpexpect.Expect { - u := url.URL{ - Scheme: "https", - Host: s.apisixHttpsTunnel.Endpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecure, - ServerName: host, - RootCAs: ca, - Certificates: certs, - }, - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(GinkgoT()), - ), - }) -} - -// APISIXGatewayServiceEndpoint returns the apisix http gateway endpoint. -func (s *Scaffold) APISIXGatewayServiceEndpoint() string { - return s.apisixHttpTunnel.Endpoint() -} - -// RestartAPISIXDeploy delete apisix pod and wait new pod be ready -func (s *Scaffold) RestartAPISIXDeploy() { - s.shutdownApisixTunnel() - pods, err := k8s.ListPodsE(s.t, s.kubectlOptions, metav1.ListOptions{ - LabelSelector: "app=apisix-deployment-e2e-test", - }) - Expect(err).NotTo(HaveOccurred(), "list apisix pods") - for _, pod := range pods { - err = s.KillPod(pod.Name) - Expect(err).NotTo(HaveOccurred(), "kill apisix pod") - } - err = framework.WaitPodsAvailable(s.t, s.kubectlOptions, metav1.ListOptions{ - LabelSelector: "app.kubernetes.io/name=apisix", - }) - Expect(err).ToNot(HaveOccurred(), "waiting for gateway pod ready") - err = s.newAPISIXTunnels() - Expect(err).NotTo(HaveOccurred(), "renew apisix tunnels") -} - -func (s *Scaffold) RestartIngressControllerDeploy() { - pods, err := k8s.ListPodsE(s.t, s.kubectlOptions, metav1.ListOptions{ - LabelSelector: "app=ingress-apisix-controller-deployment-e2e-test", - }) - Expect(err).NotTo(HaveOccurred(), "list ingress-controller pods") - - for _, pod := range pods { - err = s.KillPod(pod.Name) - Expect(err).NotTo(HaveOccurred(), "kill ingress-controller pod") - } -} - -func (s *Scaffold) beforeEach() { - var err error - s.UploadLicense() - s.namespace = fmt.Sprintf("ingress-apisix-e2e-tests-%s-%d", s.opts.Name, time.Now().Nanosecond()) - s.kubectlOptions = &k8s.KubectlOptions{ - ConfigPath: s.opts.Kubeconfig, - Namespace: s.namespace, - } - if s.opts.ControllerName == "" { - s.opts.ControllerName = fmt.Sprintf("%s/%d", DefaultControllerName, time.Now().Nanosecond()) - } - s.finalizers = nil - if s.label == nil { - s.label = make(map[string]string) - } - if s.opts.NamespaceSelectorLabel != nil { - for k, v := range s.opts.NamespaceSelectorLabel { - if len(v) > 0 { - s.label[k] = v[0] - } - } - } else { - s.label["apisix.ingress.watch"] = s.namespace - } - - // Initialize additionalGatewayGroups map - s.additionalGatewayGroups = make(map[string]*GatewayGroupResources) - - var nsLabel map[string]string - if !s.opts.DisableNamespaceLabel { - nsLabel = s.label - } - k8s.CreateNamespaceWithMetadata(s.t, s.kubectlOptions, metav1.ObjectMeta{Name: s.namespace, Labels: nsLabel}) - - s.nodes, err = k8s.GetReadyNodesE(s.t, s.kubectlOptions) - Expect(err).NotTo(HaveOccurred(), "getting ready nodes") - - s.gatewaygroupid = s.CreateNewGatewayGroupWithIngress() - s.Logf("gateway group id: %s", s.gatewaygroupid) - - s.opts.APISIXAdminAPIKey = s.GetAdminKey(s.gatewaygroupid) - - s.Logf("apisix admin api key: %s", s.opts.APISIXAdminAPIKey) - - e := utils.ParallelExecutor{} - - e.Add(func() { - s.deployDataplane() - s.deployIngress() - s.initDataPlaneClient() - }) - e.Add(s.DeployTestService) - e.Wait() -} - -func (s *Scaffold) initDataPlaneClient() { - var err error - s.apisixCli, err = dashboard.NewClient() - Expect(err).NotTo(HaveOccurred(), "creating apisix client") - - url := fmt.Sprintf("http://%s/apisix/admin", s.GetDashboardEndpoint()) - - s.Logf("apisix admin: %s", url) - - err = s.apisixCli.AddCluster(context.Background(), &dashboard.ClusterOptions{ - Name: "default", - ControllerName: s.opts.ControllerName, - Labels: map[string]string{"k8s/controller-name": s.opts.ControllerName}, - BaseURL: url, - AdminKey: s.AdminKey(), - }) - Expect(err).NotTo(HaveOccurred(), "adding cluster") - - httpsURL := fmt.Sprintf("https://%s/apisix/admin", s.GetDashboardEndpointHTTPS()) - err = s.apisixCli.AddCluster(context.Background(), &dashboard.ClusterOptions{ - Name: "default-https", - BaseURL: httpsURL, - AdminKey: s.AdminKey(), - SkipTLSVerify: true, - }) - Expect(err).NotTo(HaveOccurred(), "adding cluster") -} - -func (s *Scaffold) DefaultDataplaneResource() dashboard.Cluster { - return s.apisixCli.Cluster("default") -} - -func (s *Scaffold) DefaultDataplaneResourceHTTPS() dashboard.Cluster { - return s.apisixCli.Cluster("default-https") -} - -func (s *Scaffold) DataPlaneClient() dashboard.Dashboard { - return s.apisixCli -} - -func (s *Scaffold) DeployTestService() { - var err error - - s.httpbinService, err = s.newHTTPBIN() - Expect(err).NotTo(HaveOccurred(), "creating httpbin service") - s.EnsureNumEndpointsReady(s.t, s.httpbinService.Name, 1) -} - -func (s *Scaffold) afterEach() { - defer GinkgoRecover() - s.DeleteGatewayGroup(s.gatewaygroupid) - - if CurrentSpecReport().Failed() { - if os.Getenv("TEST_ENV") == "CI" { - _, _ = fmt.Fprintln(GinkgoWriter, "Dumping namespace contents") - _, _ = k8s.RunKubectlAndGetOutputE(GinkgoT(), s.kubectlOptions, "get", "deploy,sts,svc,pods,gatewayproxy") - _, _ = k8s.RunKubectlAndGetOutputE(GinkgoT(), s.kubectlOptions, "describe", "pods") - } - - output := s.GetDeploymentLogs("apisix-ingress-controller") - if output != "" { - _, _ = fmt.Fprintln(GinkgoWriter, output) - } - } - - // Delete all additional namespaces - for _, resources := range s.additionalGatewayGroups { - err := s.CleanupAdditionalGatewayGroup(resources.GatewayGroupID) - Expect(err).NotTo(HaveOccurred(), "cleaning up additional gateway group") - } - - // if the test case is successful, just delete namespace - err := k8s.DeleteNamespaceE(s.t, s.kubectlOptions, s.namespace) - Expect(err).NotTo(HaveOccurred(), "deleting namespace "+s.namespace) - - for i := len(s.finalizers) - 1; i >= 0; i-- { - runWithRecover(s.finalizers[i]) - } - - // Wait for a while to prevent the worker node being overwhelming - // (new cases will be run). - time.Sleep(3 * time.Second) -} - -func runWithRecover(f func()) { - defer func() { - r := recover() - if r == nil { - return - } - err, ok := r.(error) - if ok { - // just ignore already closed channel - if strings.Contains(err.Error(), "close of closed channel") { - return - } - } - panic(r) - }() - f() -} - -func (s *Scaffold) GetDeploymentLogs(name string) string { - cli, err := k8s.GetKubernetesClientE(s.t) - Expect(err).NotTo(HaveOccurred(), "getting k8s client") - - pods, err := cli.CoreV1().Pods(s.namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: "app=" + name, - }) - if err != nil { - return "" - } - var buf strings.Builder - for _, pod := range pods.Items { - buf.WriteString(fmt.Sprintf("=== pod: %s ===\n", pod.Name)) - logs, err := cli.CoreV1().RESTClient().Get(). - Resource("pods"). - Namespace(s.namespace). - Name(pod.Name).SubResource("log"). - Do(context.TODO()). - Raw() - if err == nil { - buf.Write(logs) - } - buf.WriteByte('\n') - } - return buf.String() -} - -func (s *Scaffold) addFinalizers(f func()) { - s.finalizers = append(s.finalizers, f) -} - -// FormatRegistry replace default registry to custom registry if exist -func (s *Scaffold) FormatRegistry(workloadTemplate string) string { - customRegistry, isExist := os.LookupEnv("REGISTRY") - if isExist { - return strings.ReplaceAll(workloadTemplate, "127.0.0.1:5000", customRegistry) - } else { - return workloadTemplate - } -} - -func waitExponentialBackoff(condFunc func() (bool, error)) error { - backoff := wait.Backoff{ - Duration: 500 * time.Millisecond, - Factor: 2, - Steps: 8, - } - return wait.ExponentialBackoff(backoff, condFunc) -} - -func (s *Scaffold) DeleteResource(resourceType, name string) error { - return k8s.RunKubectlE(s.t, s.kubectlOptions, "delete", resourceType, name) -} - -func (s *Scaffold) NamespaceSelectorLabelStrings() []string { - var labels []string - if s.opts.NamespaceSelectorLabel != nil { - for k, v := range s.opts.NamespaceSelectorLabel { - for _, v0 := range v { - labels = append(labels, fmt.Sprintf("%s=%s", k, v0)) - } - } - } else { - for k, v := range s.label { - labels = append(labels, fmt.Sprintf("%s=%s", k, v)) - } - } - return labels -} - -func (s *Scaffold) NamespaceSelectorLabel() map[string][]string { - return s.opts.NamespaceSelectorLabel -} -func (s *Scaffold) labelSelector(label string) metav1.ListOptions { - return metav1.ListOptions{ - LabelSelector: label, - } -} - -func (s *Scaffold) GetControllerName() string { - return s.opts.ControllerName -} - -// CreateAdditionalGatewayGroup creates a new gateway group and deploys a dataplane for it. -// It returns the gateway group ID and namespace name where the dataplane is deployed. -func (s *Scaffold) CreateAdditionalGatewayGroup(namePrefix string) (string, string, error) { - // Create a new namespace for this gateway group - additionalNS := fmt.Sprintf("%s-%d", namePrefix, time.Now().Unix()) - - // Create namespace with the same labels - var nsLabel map[string]string - if !s.opts.DisableNamespaceLabel { - nsLabel = s.label - } - k8s.CreateNamespaceWithMetadata(s.t, s.kubectlOptions, metav1.ObjectMeta{Name: additionalNS, Labels: nsLabel}) - - // Create new kubectl options for the new namespace - kubectlOpts := &k8s.KubectlOptions{ - ConfigPath: s.opts.Kubeconfig, - Namespace: additionalNS, - } - - // Create a new gateway group - gatewayGroupID := s.CreateNewGatewayGroupWithIngress() - s.Logf("additional gateway group id: %s in namespace %s", gatewayGroupID, additionalNS) - - // Get the admin key for this gateway group - adminKey := s.GetAdminKey(gatewayGroupID) - s.Logf("additional gateway group admin api key: %s", adminKey) - - // Store gateway group info - resources := &GatewayGroupResources{ - GatewayGroupID: gatewayGroupID, - Namespace: additionalNS, - AdminAPIKey: adminKey, - } - - serviceName := fmt.Sprintf("api7ee3-apisix-gateway-%s", namePrefix) - - // Deploy dataplane for this gateway group - svc := s.DeployGateway(framework.DataPlaneDeployOptions{ - GatewayGroupID: gatewayGroupID, - Namespace: additionalNS, - Name: serviceName, - ServiceName: serviceName, - DPManagerEndpoint: framework.DPManagerTLSEndpoint, - SetEnv: true, - SSLKey: framework.TestKey, - SSLCert: framework.TestCert, - TLSEnabled: true, - ForIngressGatewayGroup: true, - ServiceHTTPPort: 9080, - ServiceHTTPSPort: 9443, - }) - - resources.DataplaneService = svc - - // Create tunnels for the dataplane - httpTunnel, httpsTunnel, err := s.createDataplaneTunnels(svc, kubectlOpts, serviceName) - if err != nil { - return "", "", err - } - - resources.HttpTunnel = httpTunnel - resources.HttpsTunnel = httpsTunnel - - // Store in the map - s.additionalGatewayGroups[gatewayGroupID] = resources - - return gatewayGroupID, additionalNS, nil -} - -// createDataplaneTunnels creates HTTP and HTTPS tunnels for a dataplane service. -// It's extracted from newAPISIXTunnels to be reusable for additional gateway groups. -func (s *Scaffold) createDataplaneTunnels( - svc *corev1.Service, - kubectlOpts *k8s.KubectlOptions, - serviceName string, -) (*k8s.Tunnel, *k8s.Tunnel, error) { - var ( - httpNodePort int - httpsNodePort int - httpPort int - httpsPort int - ) - - for _, port := range svc.Spec.Ports { - switch port.Name { - case "http": - httpNodePort = int(port.NodePort) - httpPort = int(port.Port) - case "https": - httpsNodePort = int(port.NodePort) - httpsPort = int(port.Port) - } - } - - httpTunnel := k8s.NewTunnel(kubectlOpts, k8s.ResourceTypeService, serviceName, - httpNodePort, httpPort) - httpsTunnel := k8s.NewTunnel(kubectlOpts, k8s.ResourceTypeService, serviceName, - httpsNodePort, httpsPort) - - if err := httpTunnel.ForwardPortE(s.t); err != nil { - return nil, nil, err - } - s.addFinalizers(httpTunnel.Close) - - if err := httpsTunnel.ForwardPortE(s.t); err != nil { - httpTunnel.Close() - return nil, nil, err - } - s.addFinalizers(httpsTunnel.Close) - - return httpTunnel, httpsTunnel, nil -} - -// GetAdditionalGatewayGroup returns resources associated with a specific Gateway group -func (s *Scaffold) GetAdditionalGatewayGroup(gatewayGroupID string) (*GatewayGroupResources, bool) { - resources, exists := s.additionalGatewayGroups[gatewayGroupID] - return resources, exists -} - -// NewAPISIXClientForGatewayGroup creates an HTTP client for a specific Gateway group -func (s *Scaffold) NewAPISIXClientForGatewayGroup(gatewayGroupID string) (*httpexpect.Expect, error) { - resources, exists := s.additionalGatewayGroups[gatewayGroupID] - if !exists { - return nil, fmt.Errorf("gateway group %s not found", gatewayGroupID) - } - - u := url.URL{ - Scheme: "http", - Host: resources.HttpTunnel.Endpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{}, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(GinkgoT()), - ), - }), nil -} - -// NewAPISIXHttpsClientForGatewayGroup creates an HTTPS client for a specific Gateway group -func (s *Scaffold) NewAPISIXHttpsClientForGatewayGroup(gatewayGroupID string, host string) (*httpexpect.Expect, error) { - resources, exists := s.additionalGatewayGroups[gatewayGroupID] - if !exists { - return nil, fmt.Errorf("gateway group %s not found", gatewayGroupID) - } - - u := url.URL{ - Scheme: "https", - Host: resources.HttpsTunnel.Endpoint(), - } - return httpexpect.WithConfig(httpexpect.Config{ - BaseURL: u.String(), - Client: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - // accept any certificate; for testing only! - InsecureSkipVerify: true, - ServerName: host, - }, - }, - }, - Reporter: httpexpect.NewAssertReporter( - httpexpect.NewAssertReporter(GinkgoT()), - ), - }), nil -} - -// CleanupAdditionalGatewayGroup cleans up resources associated with a specific Gateway group -func (s *Scaffold) CleanupAdditionalGatewayGroup(gatewayGroupID string) error { - resources, exists := s.additionalGatewayGroups[gatewayGroupID] - if !exists { - return fmt.Errorf("gateway group %s not found", gatewayGroupID) - } - - // Delete the gateway group - s.DeleteGatewayGroup(gatewayGroupID) - - // Delete the namespace - err := k8s.DeleteNamespaceE(s.t, &k8s.KubectlOptions{ - ConfigPath: s.opts.Kubeconfig, - Namespace: resources.Namespace, - }, resources.Namespace) - - // Remove from the map - delete(s.additionalGatewayGroups, gatewayGroupID) - - return err -} - -// GetGatewayGroupHTTPEndpoint returns the HTTP endpoint for a specific Gateway group -func (s *Scaffold) GetGatewayGroupHTTPEndpoint(gatewayGroupID string) (string, error) { - resources, exists := s.additionalGatewayGroups[gatewayGroupID] - if !exists { - return "", fmt.Errorf("gateway group %s not found", gatewayGroupID) - } - - return resources.HttpTunnel.Endpoint(), nil -} - -// GetGatewayGroupHTTPSEndpoint returns the HTTPS endpoint for a specific Gateway group -func (s *Scaffold) GetGatewayGroupHTTPSEndpoint(gatewayGroupID string) (string, error) { - resources, exists := s.additionalGatewayGroups[gatewayGroupID] - if !exists { - return "", fmt.Errorf("gateway group %s not found", gatewayGroupID) - } - - return resources.HttpsTunnel.Endpoint(), nil -} - -func (s *Scaffold) CurrentGatewayGroupID() string { - return s.gatewaygroupid -} diff --git a/test/e2e/scaffold/ssl.go b/test/e2e/scaffold/ssl.go deleted file mode 100644 index 97cb62d4e..000000000 --- a/test/e2e/scaffold/ssl.go +++ /dev/null @@ -1,228 +0,0 @@ -// 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 scaffold - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/pem" - "fmt" - "math/big" - "time" - - . "github.com/onsi/ginkgo/v2" //nolint:staticcheck - . "github.com/onsi/gomega" //nolint:staticcheck -) - -const ( - _secretTemplate = ` -apiVersion: v1 -kind: Secret -metadata: - name: %s -data: - cert: %s - key: %s -` - _kubeTlsSecretTemplate = ` -apiVersion: v1 -kind: Secret -metadata: - name: %s -data: - tls.crt: %s - tls.key: %s -` - _clientCASecretTemplate = ` -apiVersion: v1 -kind: Secret -metadata: - name: %s -data: - cert: %s -` -) - -// NewSecret new a k8s secret -func (s *Scaffold) NewSecret(name, cert, key string) error { - certBase64 := base64.StdEncoding.EncodeToString([]byte(cert)) - keyBase64 := base64.StdEncoding.EncodeToString([]byte(key)) - secret := fmt.Sprintf(_secretTemplate, name, certBase64, keyBase64) - if err := s.CreateResourceFromString(secret); err != nil { - return err - } - return nil -} - -// NewKubeTlsSecret new a kube style tls secret -func (s *Scaffold) NewKubeTlsSecret(name, cert, key string) error { - certBase64 := base64.StdEncoding.EncodeToString([]byte(cert)) - keyBase64 := base64.StdEncoding.EncodeToString([]byte(key)) - secret := fmt.Sprintf(_kubeTlsSecretTemplate, name, certBase64, keyBase64) - if err := s.CreateResourceFromString(secret); err != nil { - return err - } - return nil -} - -// NewClientCASecret new a k8s secret -func (s *Scaffold) NewClientCASecret(name, cert, key string) error { - certBase64 := base64.StdEncoding.EncodeToString([]byte(cert)) - secret := fmt.Sprintf(_clientCASecretTemplate, name, certBase64) - if err := s.CreateResourceFromString(secret); err != nil { - return err - } - return nil -} - -func (s *Scaffold) GenerateCert(t GinkgoTInterface, dnsNames []string) (certPemBytes, privPemBytes bytes.Buffer) { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - Expect(err).ToNot(HaveOccurred()) - pub := priv.Public() - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - Expect(err).ToNot(HaveOccurred()) - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - - DNSNames: dnsNames, - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, priv) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&certPemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - Expect(err).ToNot(HaveOccurred()) - - privBytes, err := x509.MarshalPKCS8PrivateKey(priv) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&privPemBytes, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) - Expect(err).ToNot(HaveOccurred()) - - return certPemBytes, privPemBytes -} - -// GenerateMACert used for generate MutualAuthCerts -func (s *Scaffold) GenerateMACert( - t GinkgoTInterface, - dnsNames []string, -) ( - caCertBytes, serverCertBytes, serverKeyBytes, clientCertBytes, clientKeyBytes bytes.Buffer, -) { - // CA cert - caKey, err := rsa.GenerateKey(rand.Reader, 2048) - Expect(err).ToNot(HaveOccurred()) - caPub := caKey.Public() - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - Expect(err).ToNot(HaveOccurred()) - - caTemplate := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: dnsNames[0] + "-ca", - Organization: []string{"Acme Co"}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } - - caTemplate.IsCA = true - caTemplate.KeyUsage |= x509.KeyUsageCertSign - - caBytes, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, caPub, caKey) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&caCertBytes, &pem.Block{Type: "CERTIFICATE", Bytes: caBytes}) - Expect(err).ToNot(HaveOccurred()) - - // Server cert - serverKey, err := rsa.GenerateKey(rand.Reader, 2048) - Expect(err).ToNot(HaveOccurred()) - - serialNumber, err = rand.Int(rand.Reader, serialNumberLimit) - Expect(err).ToNot(HaveOccurred()) - - serverTemplate := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: dnsNames[0], - Organization: []string{"Acme Co"}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } - - serverBytes, err := x509.CreateCertificate(rand.Reader, &serverTemplate, &caTemplate, &serverKey.PublicKey, caKey) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&serverCertBytes, &pem.Block{Type: "CERTIFICATE", Bytes: serverBytes}) - Expect(err).ToNot(HaveOccurred()) - serverKeyBytesD, err := x509.MarshalPKCS8PrivateKey(serverKey) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&serverKeyBytes, &pem.Block{Type: "PRIVATE KEY", Bytes: serverKeyBytesD}) - Expect(err).ToNot(HaveOccurred()) - - // Client cert - clientKey, err := rsa.GenerateKey(rand.Reader, 2048) - Expect(err).ToNot(HaveOccurred()) - - serialNumber, err = rand.Int(rand.Reader, serialNumberLimit) - Expect(err).ToNot(HaveOccurred()) - - clientTemplate := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: dnsNames[0] + "-client", - Organization: []string{"Acme Co"}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } - - clientBytes, err := x509.CreateCertificate(rand.Reader, &clientTemplate, &caTemplate, &clientKey.PublicKey, caKey) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&clientCertBytes, &pem.Block{Type: "CERTIFICATE", Bytes: clientBytes}) - Expect(err).ToNot(HaveOccurred()) - clientKeyBytesD, err := x509.MarshalPKCS8PrivateKey(clientKey) - Expect(err).ToNot(HaveOccurred()) - err = pem.Encode(&clientKeyBytes, &pem.Block{Type: "PRIVATE KEY", Bytes: clientKeyBytesD}) - Expect(err).ToNot(HaveOccurred()) - - return caCertBytes, serverCertBytes, serverKeyBytes, clientCertBytes, clientKeyBytes -} diff --git a/test/utils/utils.go b/test/utils/utils.go deleted file mode 100644 index deedc220d..000000000 --- a/test/utils/utils.go +++ /dev/null @@ -1,136 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package utils - -import ( - "fmt" - "os" - "os/exec" - "strings" - - . "github.com/onsi/ginkgo/v2" //nolint:golint,revive,staticcheck -) - -const ( - prometheusOperatorVersion = "v0.72.0" - prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + - "releases/download/%s/bundle.yaml" - - certmanagerVersion = "v1.14.4" - certmanagerURLTmpl = "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" -) - -func warnError(err error) { - _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err) -} - -// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. -func InstallPrometheusOperator() error { - url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) - cmd := exec.Command("kubectl", "create", "-f", url) - _, err := Run(cmd) - return err -} - -// Run executes the provided command within this context -func Run(cmd *exec.Cmd) ([]byte, error) { - dir, _ := GetProjectDir() - cmd.Dir = dir - - if err := os.Chdir(cmd.Dir); err != nil { - _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err) - } - - cmd.Env = append(os.Environ(), "GO111MODULE=on") - command := strings.Join(cmd.Args, " ") - _, _ = fmt.Fprintf(GinkgoWriter, "running: %s\n", command) - output, err := cmd.CombinedOutput() - if err != nil { - return output, fmt.Errorf("%s failed with error: (%v) %s", command, err, string(output)) - } - - return output, nil -} - -// UninstallPrometheusOperator uninstalls the prometheus -func UninstallPrometheusOperator() { - url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) - cmd := exec.Command("kubectl", "delete", "-f", url) - if _, err := Run(cmd); err != nil { - warnError(err) - } -} - -// UninstallCertManager uninstalls the cert manager -func UninstallCertManager() { - url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) - cmd := exec.Command("kubectl", "delete", "-f", url) - if _, err := Run(cmd); err != nil { - warnError(err) - } -} - -// InstallCertManager installs the cert manager bundle. -func InstallCertManager() error { - url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) - cmd := exec.Command("kubectl", "apply", "-f", url) - if _, err := Run(cmd); err != nil { - return err - } - // Wait for cert-manager-webhook to be ready, which can take time if cert-manager - // was re-installed after uninstalling on a cluster. - cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook", - "--for", "condition=Available", - "--namespace", "cert-manager", - "--timeout", "5m", - ) - - _, err := Run(cmd) - return err -} - -// LoadImageToKindClusterWithName loads a local docker image to the kind cluster -func LoadImageToKindClusterWithName(name string) error { - cluster := "kind" - if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { - cluster = v - } - kindOptions := []string{"load", "docker-image", name, "--name", cluster} - cmd := exec.Command("kind", kindOptions...) - _, err := Run(cmd) - return err -} - -// GetNonEmptyLines converts given command output string into individual objects -// according to line breakers, and ignores the empty elements in it. -func GetNonEmptyLines(output string) []string { - var res []string - elements := strings.Split(output, "\n") - for _, element := range elements { - if element != "" { - res = append(res, element) - } - } - - return res -} - -// GetProjectDir will return the directory where the project is -func GetProjectDir() (string, error) { - wd, err := os.Getwd() - if err != nil { - return wd, err - } - wd = strings.ReplaceAll(wd, "/test/e2e", "") - return wd, nil -}